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

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-linux.cc,v $
  21.  * Revision 1.8  1999/04/02 16:44:30  mueller
  22.  * Removed 'revisionDate' because it is not available in general.
  23.  *
  24.  * Revision 1.7  1999/03/27 20:52:56  mueller
  25.  * Adapted to slightly changed interface.
  26.  *
  27.  * Revision 1.6  1999/02/06 20:41:44  mueller
  28.  * Improved error message.
  29.  *
  30.  * Revision 1.5  1998/09/06 13:34:22  mueller
  31.  * Use 'message()' for printing messages.
  32.  *
  33.  * Revision 1.4  1998/08/13 19:13:28  mueller
  34.  * Added member function 'timout()' to set timeout of SCSI commands.
  35.  *
  36.  * Revision 1.3  1998/08/07 12:36:04  mueller
  37.  * Added enabling command transformation for emulated host adaptor (ATAPI).
  38.  *
  39.  */
  40. static char rcsid[] = "$Id: ScsiIf-linux.cc,v 1.8 1999/04/02 16:44:30 mueller Exp mueller $";
  41. #include <config.h>
  42. #include <stdio.h>
  43. #include <fcntl.h>
  44. #include <unistd.h>
  45. #include <errno.h>
  46. #include <string.h>
  47. #include <assert.h>
  48. #include <sys/ioctl.h>
  49. #include <linux/../scsi/sg.h>  /* cope with silly includes */
  50. #include <asm/param.h> // for HZ
  51. #include "ScsiIf.h"
  52. #include "util.h"
  53. #include "sg_err.h"
  54. /* Runtime selection to obtain the best features available from the
  55.    Linux SCSI generic (sg) driver taken from:
  56. *  Copyright (C) 1999 D. Gilbert
  57. *  This program is free software; you can redistribute it and/or modify
  58. *  it under the terms of the GNU General Public License as published by
  59. *  the Free Software Foundation; either version 2, or (at your option)
  60. *  any later version.
  61.    This program is meant as an example to application writers who wish
  62.    to use the Linux sg driver. Linux changed its sg driver in kernel
  63.    2.2.6 . The new version has extra features that application writers
  64.    may wish to use but not at the expense of backward compatibility with
  65.    the original driver. Also if an application is distributed in binary
  66.    code, runtime selection is needed in the application code interfacing
  67.    to sg in order to cope with the various combinations:
  68.    App compiled with    App binary run on     Notes
  69.    ----------------------------------------------------------------------
  70.    new sg.h             new driver
  71.    new sg.h             original driver       backward compatibility mode
  72.    original sg.h        new driver            "forward" compatibility mode
  73.    original sg.h        original driver
  74.    Noteworthy features:
  75.         - forward + backward compatible from 2.0 to 2.3 series of
  76.           kernels (tested on: 2.0.27, 2.2.10, 2.3.8). Extra features
  77.           are used when available. This is done via run time selection.
  78.         - handles /usr/include/scsi bug in Redhat 6.0 + other distros
  79.         - allows device argument to be a non-sg SCSI device (eg /dev/sda)
  80.           and shows mapping to equivalent sg device
  81.         - internal scan (used for previous item when non-sg device) does
  82.           not hang on O_EXCL flag on device but steps over it.
  83.         - allows app to request reserved buffer size and shows the amount
  84.           actually reserved **
  85.         - outputs as much error information as is available
  86.         - uses categorization and sense buffer decode routines in sg_err.c
  87.         - sets SCSI command length explicitly (when available)
  88.    ** The original reserved buffer (ie SG_BIG_BUFF) is allocated one for
  89.       all users of the driver. From 2.2.6 onwards, the reserved buffer is
  90.       per file descriptor.
  91.    One assumption made here is that ioctl command numbers do not change.
  92. */
  93. #ifndef SG_GET_RESERVED_SIZE
  94. #define SG_GET_RESERVED_SIZE 0x2272
  95. #endif
  96. #ifndef SG_SET_RESERVED_SIZE
  97. #define SG_SET_RESERVED_SIZE 0x2275
  98. #endif
  99. #ifndef SG_GET_VERSION_NUM
  100. #define SG_GET_VERSION_NUM 0x2282
  101. #endif
  102. #ifndef SG_MAX_SENSE
  103. #define SG_MAX_SENSE 16
  104. #endif
  105. #define SG_RT_ORIG 0
  106. #define SG_RT_NEW32 1  /* new driver version 2.1.31 + 2.1.32 */
  107. #define SG_RT_NEW34 2  /* new driver version 2.1.34 and after */
  108. class ScsiIfImpl {
  109. public:
  110.   struct ScsiIdLun {
  111.     int mux4;
  112.     int hostUniqueId;
  113.   };
  114.   struct Sghn { /* for "forward" compatibility case */
  115.     int pack_len;    /* [o] reply_len (ie useless), ignored as input */
  116.     int reply_len;   /* [i] max length of expected reply (inc. sg_header) */
  117.     int pack_id;     /* [io] id number of packet (use ints >= 0) */
  118.     int result;      /* [o] 0==ok, else (+ve) Unix errno (best ignored) */
  119.     unsigned int twelve_byte:1; 
  120.            /* [i] Force 12 byte command length for group 6 & 7 commands  */
  121.     unsigned int target_status:5;   /* [o] scsi status from target */
  122.     unsigned int host_status:8;     /* [o] host status (see "DID" codes) */
  123.     unsigned int driver_status:8;   /* [o] driver status+suggestion */
  124.     unsigned int other_flags:10;    /* unused */
  125.     unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] Output in 3 cases:
  126.            when target_status is CHECK_CONDITION or 
  127.            when target_status is COMMAND_TERMINATED or
  128.            when (driver_status & DRIVER_SENSE) is true. */
  129.   };      /* This structure is 36 bytes long on i386 */
  130.   char *filename_; // user provided device name
  131.   char *dev_;      // actual sg device name
  132.   int fd_;
  133.   int driverVersion_;
  134.   int maxSendLen_;
  135.   char *buf_;
  136.   Sghn *bufHd_;
  137.   const char *makeDevName(int k, int do_numeric);
  138.   int openScsiDevAsSg(const char *devname);
  139.   void determineDriverVersion();
  140.   int adjustReservedBuffer(int requestedSize);
  141. };
  142. ScsiIf::ScsiIf(const char *dev)
  143. {
  144.   impl_ = new ScsiIfImpl;
  145.   impl_->filename_ = strdupCC(dev);
  146.   impl_->dev_ = NULL;
  147.   impl_->maxSendLen_ = 0;
  148.   impl_->buf_ = NULL;
  149.   impl_->bufHd_ = NULL;
  150.   
  151.   impl_->fd_ = -1;
  152.   vendor_[0] = 0;
  153.   product_[0] = 0;
  154.   revision_[0] = 0;
  155. }
  156. ScsiIf::~ScsiIf()
  157. {
  158.   if (impl_->fd_ >= 0)
  159.     close(impl_->fd_);
  160.   delete[] impl_->filename_;
  161.   delete[] impl_->dev_;
  162.   delete[] impl_->buf_;
  163.   delete impl_;
  164. }
  165. // opens and flushes scsi device
  166. // return: 0: OK
  167. //         1: device could not be opened
  168. //         2: inquiry failed
  169. int ScsiIf::init()
  170. {
  171.   int flags;
  172.   if ((impl_->fd_ = impl_->openScsiDevAsSg(impl_->filename_)) < 0) {
  173.     if (impl_->fd_ != -9999) {
  174.       message(-2, "Cannot open SG device "%s": %s",
  175.       impl_->filename_, strerror(errno));
  176.       return 1;
  177.     }
  178.     else {
  179.       message(-2, "Cannot map "%s" to a SG device.", impl_->filename_);
  180.       return 1;
  181.     }
  182.   }
  183.   impl_->determineDriverVersion();
  184.   impl_->maxSendLen_ = impl_->adjustReservedBuffer(64 * 1024);
  185.   if (impl_->maxSendLen_ < 4096) {
  186.     message(-2, "%s: Cannot reserve enough buffer space - granted size: %d",
  187.     impl_->dev_, impl_->maxSendLen_);
  188.     return 1;
  189.   }
  190.   maxDataLen_ = impl_->maxSendLen_ - sizeof(struct sg_header) - 12;
  191.   impl_->buf_ = new char[impl_->maxSendLen_];
  192.   impl_->bufHd_ = (ScsiIfImpl::Sghn *)impl_->buf_;
  193.   
  194.   flags = fcntl(impl_->fd_, F_GETFL);
  195.   fcntl(impl_->fd_, F_SETFL, flags|O_NONBLOCK);
  196.   memset(impl_->bufHd_, 0, sizeof(ScsiIfImpl::Sghn));
  197.   impl_->bufHd_->reply_len = sizeof(ScsiIfImpl::Sghn);
  198.   while (read(impl_->fd_, impl_->bufHd_, impl_->maxSendLen_) >= 0 ||
  199.  errno != EAGAIN) ;
  200.   fcntl(impl_->fd_, F_SETFL, flags);
  201. #if 0 && defined(SG_EMULATED_HOST) && defined(SG_SET_TRANSFORM)
  202.   if (ioctl(impl_->fd_, SG_EMULATED_HOST, &flags) == 0 && flags != 0) {
  203.     // emulated host adaptor for ATAPI drives
  204.     // switch on command transformation
  205.     ioctl(impl_->fd_, SG_SET_TRANSFORM, NULL);
  206.   }
  207. #endif
  208.   if (inquiry() != 0) {
  209.     return 2;
  210.   }
  211.   return 0;
  212. }
  213. // Sets given timeout value in seconds and returns old timeout.
  214. // return: old timeout
  215. int ScsiIf::timeout(int t)
  216. {
  217.   int old = ioctl(impl_->fd_, SG_GET_TIMEOUT, NULL);
  218.   int ret;
  219.   t *= HZ;
  220.   if ((ret = ioctl(impl_->fd_, SG_SET_TIMEOUT, &t)) != 0) {
  221.     message(-1, "Cannot set SCSI timeout: %s", strerror(ret));
  222.   }
  223.   return old/HZ;
  224. }
  225. // sends a scsi command and receives data
  226. // return 0: OK
  227. //        1: scsi command failed (os level, no sense data available)
  228. //        2: scsi command failed (sense data available)
  229. int ScsiIf::sendCmd(const unsigned char *cmd, int cmdLen, 
  230.     const unsigned char *dataOut, int dataOutLen,
  231.     unsigned char *dataIn, int dataInLen, int showMessage)
  232. {
  233.   int status;
  234.   int sendLen = sizeof(struct sg_header) + cmdLen + dataOutLen;
  235.   int recLen = sizeof(struct sg_header) + dataInLen;
  236.   assert(cmdLen > 0 && cmdLen <= 12);
  237.   assert(sendLen <= impl_->maxSendLen_);
  238.   memset(impl_->buf_, 0, sizeof(ScsiIfImpl::Sghn));
  239.   memcpy(impl_->buf_ + sizeof(ScsiIfImpl::Sghn), cmd, cmdLen);
  240.   if (dataOut != NULL) {
  241.     memcpy(impl_->buf_ + sizeof(ScsiIfImpl::Sghn) + cmdLen, dataOut,
  242.    dataOutLen);
  243.   }
  244.   impl_->bufHd_->reply_len   = recLen;
  245.   impl_->bufHd_->twelve_byte = (cmdLen == 12);
  246.   impl_->bufHd_->result = 0;
  247.   /* send command */
  248.   do {
  249.     status = write(impl_->fd_, impl_->buf_, sendLen);
  250.     if ((status < 0 && errno != EINTR) ||
  251. (status >= 0 && status != sendLen)) {
  252.       /* some error happened */
  253.       message(-2, "write(generic) result = %d cmd = 0x%x: %s",
  254.       status, cmd[0], strerror(errno) );
  255.       return 1;
  256.     }
  257.   } while (status < 0);
  258.   // read result
  259.   do {
  260.     status = read(impl_->fd_, impl_->buf_, recLen);
  261.     if (status < 0) {
  262.       if (errno != EINTR) {
  263. message(-2, "read(generic) failed: %s", strerror(errno));
  264. return 1;
  265.       }
  266.     }
  267.     else if (status != recLen) {
  268.       message(-2, "read(generic) did not return expected amount of data.");
  269.       return 1;
  270.     }
  271.     else if (impl_->bufHd_->result) {
  272.       // scsi command failed
  273.       switch (impl_->driverVersion_) {
  274.       case SG_RT_NEW32:
  275.       case SG_RT_NEW34:
  276. sg_chk_n_print("nSCSI command failed", impl_->bufHd_->target_status, 
  277.        impl_->bufHd_->host_status,
  278.        impl_->bufHd_->driver_status, 
  279.        impl_->bufHd_->sense_buffer);
  280. break;
  281.       default:
  282. message(-2, "read(generic) failed: %s",
  283. strerror(impl_->bufHd_->result));
  284. break;
  285.       }
  286.       return 1;
  287.     }
  288.     else if (impl_->bufHd_->sense_buffer[2] != 0) {
  289.       if (showMessage)
  290. printError();
  291.       return 2;
  292.     }
  293.   } while (status < 0);
  294.   if (dataIn != NULL && dataInLen > 0) {
  295.     memcpy(dataIn, impl_->buf_ + sizeof(ScsiIfImpl::Sghn), dataInLen);
  296.   }
  297.   return 0;
  298. }
  299. const unsigned char *ScsiIf::getSense(int &len) const
  300. {
  301.   len = 15;
  302.   return impl_->bufHd_->sense_buffer;
  303. }
  304. void ScsiIf::printError()
  305. {
  306.   switch (impl_->driverVersion_) {
  307.   case SG_RT_NEW32:
  308.   case SG_RT_NEW34:
  309.     sg_chk_n_print("nSCSI command failed", impl_->bufHd_->target_status, 
  310.    impl_->bufHd_->host_status, impl_->bufHd_->driver_status, 
  311.    impl_->bufHd_->sense_buffer);
  312.     break;
  313.   default:
  314.     sg_print_sense("nSCSI command failed", impl_->bufHd_->sense_buffer);
  315.     break;
  316.   }
  317.   //decodeSense(impl_->bufHd_->sense_buffer, 15);
  318. }
  319. int ScsiIf::inquiry()
  320. {
  321.   unsigned char cmd[6];
  322.   unsigned char result[0x2c];
  323.   int i;
  324.   cmd[0] = 0x12; // INQUIRY
  325.   cmd[1] = cmd[2] = cmd[3] = 0;
  326.   cmd[4] = 0x2c;
  327.   cmd[5] = 0;
  328.   if (sendCmd(cmd, 6, NULL, 0, result, 0x2c, 1) != 0) {
  329.     message(-2, "Inquiry command failed on "%s"", impl_->dev_);
  330.     return 1;
  331.   }
  332.   strncpy(vendor_, (char *)(result + 0x08), 8);
  333.   vendor_[8] = 0;
  334.   strncpy(product_, (char *)(result + 0x10), 16);
  335.   product_[16] = 0;
  336.   strncpy(revision_, (char *)(result + 0x20), 4);
  337.   revision_[4] = 0;
  338.   for (i = 7; i >= 0 && vendor_[i] == ' '; i--) {
  339.     vendor_[i] = 0;
  340.   }
  341.   for (i = 15; i >= 0 && product_[i] == ' '; i--) {
  342.     product_[i] = 0;
  343.   }
  344.   for (i = 3; i >= 0 && revision_[i] == ' '; i--) {
  345.     revision_[i] = 0;
  346.   }
  347.   
  348.   return 0;
  349. }
  350. ScsiIf::ScanData *ScsiIf::scan(int *len)
  351. {
  352.   *len = 0;
  353.   return NULL;
  354. }
  355. #include "ScsiIf-common.cc"
  356. void ScsiIfImpl::determineDriverVersion()
  357. {
  358.   int reserved_size = 0;
  359.   int sg_version = 0;
  360.   /* Run time selection code follows */
  361.   if (ioctl(fd_, SG_GET_RESERVED_SIZE, &reserved_size) < 0) {
  362.     driverVersion_ = SG_RT_ORIG;
  363.     message(1, "Detected old SG driver version.");
  364.   }
  365.   else if (ioctl(fd_, SG_GET_VERSION_NUM, &sg_version) < 0) {
  366.     driverVersion_ = SG_RT_NEW32;
  367.     message(1, "Detected SG driver version: 2.1.32");
  368.   }
  369.   else {
  370.     driverVersion_ = SG_RT_NEW34;
  371.     message(1, "Detected SG driver version: %d.%d.%d", sg_version / 10000,
  372.     (sg_version / 100) % 100, sg_version % 100);
  373.   }
  374. }
  375.     
  376. #define MAX_SG_DEVS 26
  377. #define SCAN_ALPHA 0
  378. #define SCAN_NUMERIC 1
  379. #define DEF_SCAN SCAN_ALPHA
  380. const char *ScsiIfImpl::makeDevName(int k, int do_numeric)
  381. {
  382.   static char filename[100];
  383.   char buf[20];
  384.   strcpy(filename, "/dev/sg");
  385.   if (do_numeric) {
  386.     sprintf(buf, "%d", k);
  387.     strcat(filename, buf);
  388.   }
  389.   else {
  390.     if (k <= 26) {
  391.       buf[0] = 'a' + (char)k;
  392.       buf[1] = '';
  393.       strcat(filename, buf);
  394.     }
  395.     else {
  396.       strcat(filename, "xxxx");
  397.     }
  398.   }
  399.   return filename;
  400. }
  401. int ScsiIfImpl::openScsiDevAsSg(const char *devname)
  402. {
  403.   int fd, bus, bbus, k;
  404.   ScsiIdLun m_idlun, mm_idlun;
  405.   int do_numeric = DEF_SCAN;
  406.   const char *fname = devname;
  407.   if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) < 0) {
  408.     if (EACCES == errno) {
  409.       if ((fd = open(fname, O_RDWR | O_NONBLOCK)) < 0)
  410. return fd;
  411.     }
  412.   }
  413.   if (ioctl(fd, SG_GET_TIMEOUT, 0) < 0) { /* not a sg device ? */
  414.     if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus) < 0) {
  415.       message(-2, "%s: Need a filename that resolves to a SCSI device.",
  416.       fname);
  417.       close(fd);
  418.       return -9999;
  419.     }
  420.     if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun) < 0) {
  421.       message(-2, "%s: Need a filename that resolves to a SCSI device (2).",
  422.       fname);
  423.       close(fd);
  424.       return -9999;
  425.     }
  426.     close(fd);
  427.     
  428.     for (k = 0; k < MAX_SG_DEVS; k++) {
  429.       fname = makeDevName(k, do_numeric);
  430.       if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) < 0) {
  431. if (EACCES == errno) 
  432.   fd = open(fname, O_RDWR | O_NONBLOCK);
  433. if (fd < 0) {
  434.   if ((ENOENT == errno) && (0 == k) && (do_numeric == DEF_SCAN)) {
  435.     do_numeric = ! DEF_SCAN;
  436.     fname = makeDevName(k, do_numeric);
  437.     if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) < 0) {
  438.       if (EACCES == errno) 
  439. fd = open(fname, O_RDWR | O_NONBLOCK);
  440.     }
  441.   }
  442.   if (fd < 0) {
  443.     if (EBUSY == errno)
  444.       continue;  /* step over if O_EXCL already on it */
  445.     else
  446.       break;
  447.   }
  448. }
  449.       }
  450.       if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bbus) < 0) {
  451. message(-2, "%s: SG: ioctl SCSI_IOCTL_GET_BUS_NUMBER failed: %s",
  452. fname, strerror(errno));
  453. close(fd);
  454. fd = -9999;
  455.       }
  456.       if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &mm_idlun) < 0) {
  457. message(-2, "%s: SG: ioctl SCSI_IOCTL_GET_IDLUN failed: %s", 
  458. fname, strerror(errno));
  459. close(fd);
  460. fd = -9999;
  461.       }
  462.       if ((bus == bbus) && 
  463.   ((m_idlun.mux4 & 0xff) == (mm_idlun.mux4 & 0xff)) &&
  464.   (((m_idlun.mux4 >> 8) & 0xff) == 
  465.    ((mm_idlun.mux4 >> 8) & 0xff)) &&
  466.   (((m_idlun.mux4 >> 16) & 0xff) == 
  467.    ((mm_idlun.mux4 >> 16) & 0xff))) {
  468. message(3, "Mapping %s to sg device: %s", devname, fname);
  469. break;
  470.       }
  471.       else {
  472. close(fd);
  473. fd = -9999;
  474.       }
  475.     }
  476.   }
  477.   if (fd >= 0) { /* everything ok, close and re-open read-write */
  478.     dev_ = strdupCC(fname);
  479.     close(fd);
  480.     return open(dev_, O_RDWR);
  481.   }
  482.   else {
  483.     return fd;
  484.   }
  485. }
  486. int ScsiIfImpl::adjustReservedBuffer(int requestedSize)
  487. {
  488.   int maxTransferLength;
  489.   switch (driverVersion_) {
  490.   case SG_RT_NEW32: /* SG_SET_RESERVED_SIZE exists but does nothing in */
  491.                     /* version 2.1.32 and 2.1.33, so harmless to try */
  492.   case SG_RT_NEW34:
  493.     if (ioctl(fd_, SG_SET_RESERVED_SIZE, &requestedSize) < 0) {
  494.       message(-2, "SG_SET_RESERVED_SIZE ioctl failed: %s", strerror(errno));
  495.       return 0;
  496.     }
  497.     if (ioctl(fd_, SG_GET_RESERVED_SIZE, &maxTransferLength) < 0) {
  498.       message(-2, "SG_GET_RESERVED_SIZE ioctl failed: %s", strerror(errno));
  499.       return 0;
  500.     }
  501.     break;
  502.   default:
  503. #ifdef SG_BIG_BUFF
  504.     maxTransferLength = SG_BIG_BUFF;
  505. #else
  506.     maxTransferLength = 4096;
  507. #endif
  508.     break;
  509.   }
  510.     
  511.   message(3, "SG: Maximum transfer length: %ld", maxTransferLength);
  512.   return maxTransferLength;
  513. }