ScsiIf-lib.cc
上传用户:weiliju62
上传日期:2007-01-06
资源大小:619k
文件大小:12k
源码类别:

SCSI/ASPI

开发平台:

MultiPlatform

  1. /*  cdrdao - write audio CD-Rs in disc-at-once mode
  2.  *
  3.  *  Copyright (C) 1998, 1999  Andreas Mueller <mueller@daneb.ping.de>
  4.  *
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with this program; if not, write to the Free Software
  17.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19. /*
  20.  * $Log: ScsiIf-lib.cc,v $
  21.  * Revision 1.2  1999/04/02 16:44:30  mueller
  22.  * Removed 'revisionDate' because it is not available in general.
  23.  *
  24.  * Revision 1.1  1999/03/27 20:55:18  mueller
  25.  * Initial revision
  26.  *
  27.  */
  28. static char rcsid[] = "$Id: ScsiIf-lib.cc,v 1.2 1999/04/02 16:44:30 mueller Exp mueller $";
  29. #include <config.h>
  30. #include <unistd.h>
  31. #include <string.h>
  32. #include <assert.h>
  33. #include <errno.h>
  34. #ifdef linux
  35. #include <fcntl.h>
  36. #include <sys/ioctl.h>
  37. #include <linux/../scsi/scsi.h>  /* cope with silly includes */
  38. #include <linux/../scsi/sg.h>    /* cope with silly includes */
  39. #endif
  40. #include "ScsiIf.h"
  41. #include "util.h"
  42. #include "xconfig.h"
  43. #include "standard.h"
  44. #include "scg/scgcmd.h"
  45. #include "scg/scsitransp.h"
  46. #define MAX_DATALEN_LIMIT (63 * 1024)
  47. #define MAX_SCAN_DATA_LEN 30
  48. class ScsiIfImpl {
  49. public:
  50.   char *dev_;
  51.   
  52.   int maxSendLen_;
  53.   SCSI *scgp_;
  54.   long timeout_;
  55.   unsigned int pageSize_;
  56.   char *pageAlignedBuffer_;
  57. #ifdef linux
  58.   const char *makeDevName(int k, int do_numeric);
  59.   const char *openScsiDevAsSg(const char *devname);
  60. #endif
  61. };
  62. ScsiIf::ScsiIf(const char *dev)
  63. {
  64.   impl_ = new ScsiIfImpl;
  65.   impl_->dev_ = strdupCC(dev);
  66.   impl_->scgp_ = NULL;
  67.   impl_->pageAlignedBuffer_ = NULL;
  68.   vendor_[0] = 0;
  69.   product_[0] = 0;
  70.   revision_[0] = 0;
  71.   set_progname("cdrdao");
  72. #if defined(HAVE_GETPAGESIZE)
  73.   impl_->pageSize_ = getpagesize();
  74. #elif defined(_SC_PAGESIZE)
  75.   impl_->pageSize_ = sysconf(_SC_PAGESIZE);
  76. #elif defined(_SC_PAGE_SIZE) // saw this on HPUX 9.05
  77.   impl_->pageSize_ = sysconf(_SC_PAGE_SIZE);
  78. #else
  79.   impl_->pageSize_ = 0;
  80. #endif
  81.   if (impl_->pageSize_ == 0) {
  82.     message(-3, "Cannot determine page size.");
  83.     impl_->pageSize_ = 1;
  84.   }
  85. }
  86. ScsiIf::~ScsiIf()
  87. {
  88.   if (impl_->scgp_ != NULL) {
  89.     close_scsi(impl_->scgp_);
  90.     impl_->scgp_ = NULL;
  91.   }
  92.   impl_->pageAlignedBuffer_ = NULL;
  93.   
  94.   delete[] impl_->dev_;
  95.   impl_->dev_ = NULL;
  96.   delete impl_;
  97. }
  98. // Opens the scsi device. Most of the code originates from cdrecord's 
  99. // initialization function.
  100. // return: 0: OK
  101. //         1: device could not be opened
  102. //         2: inquiry failed
  103. int ScsiIf::init()
  104. {
  105.   char errstr[80];
  106. #ifdef linux
  107.   if (impl_->dev_[0] == '/' && strchr(impl_->dev_, ':') == NULL) {
  108.     // we have a device name, check if it is a generic SCSI device or
  109.     // try to map it a generic SCSI device
  110.     const char *fname = impl_->openScsiDevAsSg(impl_->dev_);
  111.     if (fname != NULL) {
  112.       char *old = impl_->dev_;
  113.       impl_->dev_ = strdupCC(fname);
  114.       delete[] old;
  115.     }
  116.   }
  117. #endif
  118.   if ((impl_->scgp_ = open_scsi(impl_->dev_, errstr, sizeof(errstr), 0, 0))
  119.  == NULL) {
  120.     message(-2, "Cannot open SCSI device '%s': %s", impl_->dev_, errstr);
  121.     return 1;
  122.   }
  123.   impl_->timeout_ = impl_->scgp_->deftimeout;
  124.   maxDataLen_ = scsi_bufsize(impl_->scgp_, 0);
  125.   
  126.   if (maxDataLen_ > MAX_DATALEN_LIMIT)
  127.     maxDataLen_ = MAX_DATALEN_LIMIT;
  128.   impl_->pageAlignedBuffer_ = (char *)scsi_getbuf(impl_->scgp_, maxDataLen_);
  129.   if (impl_->pageAlignedBuffer_ == NULL) {
  130.     message(-2, "Cannot get SCSI buffer.");
  131.     return 1;
  132.   }
  133.   if (inquiry() != 0)
  134.     return 2;
  135.   return 0;
  136. }
  137. // Sets given timeout value in seconds and returns old timeout.
  138. // return: old timeout
  139. int ScsiIf::timeout(int t)
  140. {
  141.   long oldTimeout = impl_->timeout_;
  142.   if (t > 0) 
  143.     impl_->timeout_ = t;
  144.   return oldTimeout;
  145. }
  146. // sends a scsi command and receives data
  147. // return 0: OK
  148. //        1: scsi command failed (os level, no sense data available)
  149. //        2: scsi command failed (sense data available)
  150. int ScsiIf::sendCmd(const unsigned char *cmd, int cmdLen, 
  151.     const unsigned char *dataOut, int dataOutLen,
  152.     unsigned char *dataIn, int dataInLen,
  153.     int showMessage)
  154. {
  155.   assert(cmdLen > 0 && cmdLen <= 12);
  156.   assert(dataOutLen == 0 || dataInLen == 0);
  157.   assert(dataOutLen <= maxDataLen_);
  158.   assert(dataInLen <= maxDataLen_);
  159.   struct scg_cmd *scmd = impl_->scgp_->scmd;
  160.   int usedPageAlignedBuffer = 0;
  161.   memset(scmd, 0, sizeof(*scmd));
  162.   memcpy(scmd->cdb.cmd_cdb, cmd, cmdLen);
  163.   scmd->cdb_len = cmdLen;
  164.   if (dataOutLen > 0) {
  165.     if (((unsigned)dataOut % impl_->pageSize_) != 0) {
  166.       //message(0, "Use SCSI buffer for data out.");
  167.       memcpy(impl_->pageAlignedBuffer_, dataOut, dataOutLen);
  168.       scmd->addr = impl_->pageAlignedBuffer_;
  169.     }
  170.     else {
  171.       //message(0, "Data out is page aligned.");
  172.       scmd->addr = (char*)dataOut;
  173.     }
  174.     scmd->size = dataOutLen;
  175.   }
  176.   else if (dataInLen > 0) {
  177.     if (((unsigned)dataIn % impl_->pageSize_) != 0) {
  178.       //message(0, "Use SCSI buffer for data in.");
  179.       scmd->addr = impl_->pageAlignedBuffer_;
  180.       usedPageAlignedBuffer = 1;
  181.     }
  182.     else {
  183.       //message(0, "Data in is page aligned.");
  184.       scmd->addr = (char*)dataIn;
  185.     }
  186.     scmd->size = dataInLen;
  187.     scmd->flags = SCG_RECV_DATA;
  188.   }
  189.   scmd->flags |= SCG_DISRE_ENA;
  190.   scmd->timeout = impl_->timeout_;
  191.   scmd->sense_len = CCS_SENSE_LEN;
  192.   scmd->target = impl_->scgp_->target;
  193.   impl_->scgp_->cmdname = "";
  194.   impl_->scgp_->verbose = 0;
  195.   impl_->scgp_->silent = showMessage ? 0 : 1;
  196.   
  197.   if (scsicmd(impl_->scgp_) < 0) {
  198.     return scmd->sense_count > 0 ?  2 : 1;
  199.   }
  200.   if (usedPageAlignedBuffer)
  201.     memcpy(dataIn, impl_->pageAlignedBuffer_, dataInLen);
  202.   return 0;
  203. }
  204. const unsigned char *ScsiIf::getSense(int &len) const
  205. {
  206.   len = impl_->scgp_->scmd->sense_count;
  207.   return impl_->scgp_->scmd->u_sense.cmd_sense;
  208. }
  209. void ScsiIf::printError()
  210. {
  211.   scsiprinterr(impl_->scgp_);
  212. }
  213. int ScsiIf::inquiry()
  214. {
  215.   unsigned char cmd[6];
  216.   unsigned char result[0x2c];
  217.   int i;
  218.   cmd[0] = 0x12; // INQUIRY
  219.   cmd[1] = cmd[2] = cmd[3] = 0;
  220.   cmd[4] = 0x2c;
  221.   cmd[5] = 0;
  222.   if (sendCmd(cmd, 6, NULL, 0, result, 0x2c, 1) != 0) {
  223.     message (-2, "Inquiry command failed on '%s'.", impl_->dev_);
  224.     return 1;
  225.   }
  226.   strncpy(vendor_, (char *)(result + 0x08), 8);
  227.   vendor_[8] = 0;
  228.   strncpy(product_, (char *)(result + 0x10), 16);
  229.   product_[16] = 0;
  230.   strncpy(revision_, (char *)(result + 0x20), 4);
  231.   revision_[4] = 0;
  232.   for (i = 7; i >= 0 && vendor_[i] == ' '; i--) {
  233.     vendor_[i] = 0;
  234.   }
  235.   for (i = 15; i >= 0 && product_[i] == ' '; i--) {
  236.     product_[i] = 0;
  237.   }
  238.   for (i = 3; i >= 0 && revision_[i] == ' '; i--) {
  239.     revision_[i] = 0;
  240.   }
  241.   return 0;
  242. }
  243. static int scanInquiry(SCSI *scgp, unsigned char *buf, ScsiIf::ScanData *sdata)
  244. {
  245.   scg_cmd *cmd = scgp->scmd;
  246.   int i;
  247.   memset(buf, 0, 36);
  248.   memset(cmd, 0, sizeof(scg_cmd));
  249.   cmd->cdb.g0_cdb.cmd = 0x12; // INQUIRY
  250.   cmd->cdb.g0_cdb.count = 36;
  251.   cmd->addr = (char*)buf;
  252.   cmd->size = 36;
  253.   cmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
  254.   cmd->cdb_len = SC_G0_CDBLEN;
  255.   cmd->sense_len = CCS_SENSE_LEN;
  256.   scgp->silent = 1;
  257.   scgp->cmdname = "inquiry";
  258.   if (scsicmd(scgp) == 0) {
  259.     if ((buf[0] & 0x1f) == 0x04 || (buf[0] & 0x1f) == 0x05) {
  260.       sdata->bus = scgp->scsibus;
  261.       sdata->id = scgp->target;
  262.       sdata->lun = scgp->lun;
  263.       strncpy(sdata->vendor, (char *)(buf + 0x08), 8);
  264.       sdata->vendor[8] = 0;
  265.       strncpy(sdata->product, (char *)(buf + 0x10), 16);
  266.       sdata->product[16] = 0;
  267.       strncpy(sdata->revision, (char *)(buf + 0x20), 4);
  268.       sdata->revision[4] = 0;
  269.       for (i = 7; i >= 0 && sdata->vendor[i] == ' '; i--) {
  270. sdata->vendor[i] = 0;
  271.       }
  272.       for (i = 15; i >= 0 && sdata->product[i] == ' '; i--) {
  273. sdata->product[i] = 0;
  274.       }
  275.       for (i = 3; i >= 0 && sdata->revision[i] == ' '; i--) {
  276. sdata->revision[i] = 0;
  277.       }
  278.       return 1;
  279.     }
  280.   }
  281.   return 0;
  282. }
  283. ScsiIf::ScanData *ScsiIf::scan(int *len)
  284. {
  285.   ScanData *sdata = NULL;
  286.   SCSI *scgp;
  287.   unsigned char *buf;
  288.   char errstr[80];
  289.   *len = 0;
  290.   set_progname("cdrdao");
  291.   if ((scgp = open_scsi(NULL, errstr, sizeof(errstr), 0, 0)) == NULL)
  292.     return NULL;
  293.   // allocate buffer for inquiry data
  294.   if ((buf = (unsigned char *)scsi_getbuf(scgp, 100)) == NULL) {
  295.     close_scsi(scgp);
  296.     return NULL;
  297.   }
  298.   sdata = new ScanData[MAX_SCAN_DATA_LEN];
  299.   
  300.   for (scgp->scsibus = 0; scgp->scsibus < 16 && *len < MAX_SCAN_DATA_LEN;
  301.        scgp->scsibus++) {
  302.     if (scsi_havebus(scgp, scgp->scsibus)) {
  303.       scgp->lun = 0;
  304.       for (scgp->target=0; scgp->target < 16 && *len < MAX_SCAN_DATA_LEN;
  305.    scgp->target++) {
  306. if (scanInquiry(scgp, buf, &(sdata[*len])))
  307.   *len += 1;
  308.       }
  309.     }
  310.   }
  311.   
  312.   
  313.   close_scsi(scgp);
  314.   return sdata;
  315. }
  316. #include "ScsiIf-common.cc"
  317. #ifdef linux
  318. /* Function for mapping any SCSI device to the corresponding SG device.
  319.  * Taken from D. Gilbert's example code.
  320.  */
  321. #define MAX_SG_DEVS 26
  322. #define SCAN_ALPHA 0
  323. #define SCAN_NUMERIC 1
  324. #define DEF_SCAN SCAN_ALPHA
  325. const char *ScsiIfImpl::makeDevName(int k, int do_numeric)
  326. {
  327.   static char filename[100];
  328.   char buf[20];
  329.   strcpy(filename, "/dev/sg");
  330.   if (do_numeric) {
  331.     sprintf(buf, "%d", k);
  332.     strcat(filename, buf);
  333.   }
  334.   else {
  335.     if (k <= 26) {
  336.       buf[0] = 'a' + (char)k;
  337.       buf[1] = '';
  338.       strcat(filename, buf);
  339.     }
  340.     else {
  341.       strcat(filename, "xxxx");
  342.     }
  343.   }
  344.   return filename;
  345. }
  346. const char *ScsiIfImpl::openScsiDevAsSg(const char *devname)
  347. {
  348.   int fd, bus, bbus, k;
  349.   int do_numeric = DEF_SCAN;
  350.   const char *fname = devname;
  351.   struct {
  352.     int mux4;
  353.     int hostUniqueId;
  354.   } m_idlun, mm_idlun;
  355.   if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) < 0) {
  356.     if (EACCES == errno)
  357.       fd = open(fname, O_RDWR | O_NONBLOCK);
  358.   }
  359.   if (fd < 0) {
  360.     message(-2, "Cannot open "%s": %s", fname, strerror(errno));
  361.     return NULL;
  362.   }
  363.   if (ioctl(fd, SG_GET_TIMEOUT, 0) < 0) { /* not a sg device ? */
  364.     if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus) < 0) {
  365.       message(-2, "%s: Need a filename that resolves to a SCSI device.",
  366.       fname);
  367.       close(fd);
  368.       return NULL;
  369.     }
  370.     if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun) < 0) {
  371.       message(-2, "%s: Need a filename that resolves to a SCSI device (2).",
  372.       fname);
  373.       close(fd);
  374.       return NULL;
  375.     }
  376.     close(fd);
  377.     
  378.     for (k = 0; k < MAX_SG_DEVS; k++) {
  379.       fname = makeDevName(k, do_numeric);
  380.       if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) < 0) {
  381. if (EACCES == errno) 
  382.   fd = open(fname, O_RDWR | O_NONBLOCK);
  383. if (fd < 0) {
  384.   if ((ENOENT == errno) && (0 == k) && (do_numeric == DEF_SCAN)) {
  385.     do_numeric = ! DEF_SCAN;
  386.     fname = makeDevName(k, do_numeric);
  387.     if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) < 0) {
  388.       if (EACCES == errno) 
  389. fd = open(fname, O_RDWR | O_NONBLOCK);
  390.     }
  391.   }
  392.   if (fd < 0) {
  393.     if (EBUSY == errno)
  394.       continue;  /* step over if O_EXCL already on it */
  395.     else
  396.       break;
  397.   }
  398. }
  399.       }
  400.       if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bbus) < 0) {
  401. message(-2, "%s: SG: ioctl SCSI_IOCTL_GET_BUS_NUMBER failed: %s",
  402. fname, strerror(errno));
  403. close(fd);
  404. fd = -9999;
  405.       }
  406.       if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &mm_idlun) < 0) {
  407. message(-2, "%s: SG: ioctl SCSI_IOCTL_GET_IDLUN failed: %s", 
  408. fname, strerror(errno));
  409. close(fd);
  410. fd = -9999;
  411.       }
  412.       if ((bus == bbus) && 
  413.   ((m_idlun.mux4 & 0xff) == (mm_idlun.mux4 & 0xff)) &&
  414.   (((m_idlun.mux4 >> 8) & 0xff) == 
  415.    ((mm_idlun.mux4 >> 8) & 0xff)) &&
  416.   (((m_idlun.mux4 >> 16) & 0xff) == 
  417.    ((mm_idlun.mux4 >> 16) & 0xff))) {
  418. message(3, "Mapping %s to sg device: %s", devname, fname);
  419. break;
  420.       }
  421.       else {
  422. close(fd);
  423. fd = -9999;
  424.       }
  425.     }
  426.   }
  427.   if (fd >= 0) {
  428.     close(fd);
  429.     return fname;
  430.   }
  431.   else {
  432.     message(-2, "Cannot map "%s" to a SG device.", devname);
  433.     return NULL;
  434.   }
  435. }
  436. #endif /* linux */