// -*-c++-*-
/**********************************************************************
 *     EPSCAN, an EPSON ES-1200 scanner driver     
 *     Adam P. Jenkins <ajenkins@cs.umass.edu>
 *     
 *     This program is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License
 *     as published by the Free Software Foundation; either version 2
 *     of the License, or (at your option) any later version.
 *     
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *     
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/

// A class to implement just the SCSI commands understood by EPSON
// scanners. These are the INQUIRY, TEST UNIT READY, REQUEST SENSE,
// RECEIVE, and SEND commands.

#ifndef _Scsi_h
#define _Scsi_h

#include <sys/types.h>
#include <scsi/sg.h>

#include "IODevice.h"


class ScsiDevice : public IODevice {
 public:
   ScsiDevice(const char *device); // Initialize with device open
   ScsiDevice(void);		   // use open to open a device
   ~ScsiDevice(void);

   bool ok(void) const;		// return true if device is open


   // reads len bytes,  Returns number of bytes
   // read, or -1 on error
   int receive(const int len);
   int receive(const int len, u_char *buf);
   const u_char *getRbuf(void);

   int send(const u_char *src, const int len);
   int sendf(const char *format, ...); // send string to device

   int open(const char *device); // use to open a scsi device
   int close(void);		 // close the scsi device
   int initialize(void);	 // initialize the device

   // These three should be private, once testing is over.
   int testUnitReady(void);	// TEST UNIT READY
   const u_char *requestSense(void);	// Sense Key
   const u_char *inquiry();		// return ID string
   const char *strError() const { return errStr; }
   
private:
   int Fd;			// Device file id

   // SCSI commands
   enum  SCSI_CMDS {
      TESTUNITREADY_CMD = 0x00,
      INQUIRY_CMD = 0x12,
      REQUESTSENSE_CMD = 0x03,
      RECEIVE_CMD = 0x08,
      SEND_CMD = 0x0a
   };


   int cmdLen(const u_char cmd) const;

   // scsiXfer performs a SCSI command.
   int scsiXfer(const int cmd_len, const u_char *src, const int send_len,
		const u_char *dest, const int reply_len);

   int handleError(const u_char *sense_data);
   char errStr[200];		// store error strings
   void error(char *format, ...);
};

// Might make this do more in the future, but it's currently just to
// have a way to check if the constructor successfuly opened the device.
inline bool
ScsiDevice::ok(void) const
{
   return (Fd >= 0);
}


// the top 3 bits of a command byte indicate the length of the command.
// SCSI2 standard, 7.2.1
inline int
ScsiDevice::cmdLen(const u_char cmd) const
{
   int c = (cmd >> 5) & 0x07;

   switch (c)
   {
    case 0:
      return 6;
    case 1:
    case 2:
      return 10;
    case 5:
      return 12;
    default:
      return -1;
   }
}

#endif // Scsi_h
