Back to index

lshw  02.16
scsi.cc
Go to the documentation of this file.
00001 #include "version.h"
00002 #include "mem.h"
00003 #include "cdrom.h"
00004 #include "disk.h"
00005 #include "osutils.h"
00006 #include "heuristics.h"
00007 #include "sysfs.h"
00008 #include <glob.h>
00009 #include <sys/types.h>
00010 #include <sys/stat.h>
00011 #include <sys/ioctl.h>
00012 #include <fcntl.h>
00013 #include <unistd.h>
00014 #include <dirent.h>
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <string.h>
00018 #include <scsi/sg.h>
00019 #include <scsi/scsi.h>
00020 #ifndef MKDEV
00021 #include <linux/kdev_t.h>
00022 #endif
00023 #ifndef MINOR
00024 #include <linux/kdev_t.h>
00025 #endif
00026 
00027 #include <string>
00028 #include <map>
00029 
00030 __ID("@(#) $Id: scsi.cc 2433 2012-01-10 22:01:30Z lyonel $");
00031 
00032 #define SG_X "/dev/sg%d"
00033 #define SG_MAJOR 21
00034 
00035 #ifndef SCSI_IOCTL_GET_PCI
00036 #define SCSI_IOCTL_GET_PCI 0x5387
00037 #endif
00038 
00039 #define OPEN_FLAG O_RDWR
00040 
00041 #define SENSE_BUFF_LEN 32
00042 #define INQ_REPLY_LEN 96
00043 #define INQ_CMD_CODE 0x12
00044 #define INQ_CMD_LEN 6
00045 #define INQ_PAGE_SERIAL 0x80
00046 
00047 #define SENSE_REPLY_LEN 96
00048 #define SENSE_CMD_CODE 0x1a
00049 #define SENSE_CMD_LEN 6
00050 #define SENSE_PAGE_SERIAL 0x80
00051 
00052 #define MX_ALLOC_LEN 255
00053 #define EBUFF_SZ 256
00054 
00055 /* Some of the following error/status codes are exchanged between the
00056    various layers of the SCSI sub-system in Linux and should never
00057    reach the user. They are placed here for completeness. What appears
00058    here is copied from drivers/scsi/scsi.h which is not visible in
00059    the user space. */
00060 
00061 #ifndef SCSI_CHECK_CONDITION
00062 /* Following are the "true" SCSI status codes. Linux has traditionally
00063    used a 1 bit right and masked version of these. So now CHECK_CONDITION
00064    and friends (in <scsi/scsi.h>) are deprecated. */
00065 #define SCSI_CHECK_CONDITION 0x2
00066 #define SCSI_CONDITION_MET 0x4
00067 #define SCSI_BUSY 0x8
00068 #define SCSI_IMMEDIATE 0x10
00069 #define SCSI_IMMEDIATE_CONDITION_MET 0x14
00070 #define SCSI_RESERVATION_CONFLICT 0x18
00071 #define SCSI_COMMAND_TERMINATED 0x22
00072 #define SCSI_TASK_SET_FULL 0x28
00073 #define SCSI_ACA_ACTIVE 0x30
00074 #define SCSI_TASK_ABORTED 0x40
00075 #endif
00076 
00077 /* The following are 'host_status' codes */
00078 #ifndef DID_OK
00079 #define DID_OK 0x00
00080 #endif
00081 #ifndef DID_NO_CONNECT
00082 #define DID_NO_CONNECT 0x01                       /* Unable to connect before timeout */
00083 #define DID_BUS_BUSY 0x02                         /* Bus remain busy until timeout */
00084 #define DID_TIME_OUT 0x03                         /* Timed out for some other reason */
00085 #define DID_BAD_TARGET 0x04                       /* Bad target (id?) */
00086 #define DID_ABORT 0x05                            /* Told to abort for some other reason */
00087 #define DID_PARITY 0x06                           /* Parity error (on SCSI bus) */
00088 #define DID_ERROR 0x07                            /* Internal error */
00089 #define DID_RESET 0x08                            /* Reset by somebody */
00090 #define DID_BAD_INTR 0x09                         /* Received an unexpected interrupt */
00091 #define DID_PASSTHROUGH 0x0a                      /* Force command past mid-level */
00092 #define DID_SOFT_ERROR 0x0b                       /* The low-level driver wants a retry */
00093 #endif
00094 
00095 /* These defines are to isolate applictaions from kernel define changes */
00096 #define SG_ERR_DID_OK           DID_OK
00097 #define SG_ERR_DID_NO_CONNECT   DID_NO_CONNECT
00098 #define SG_ERR_DID_BUS_BUSY     DID_BUS_BUSY
00099 #define SG_ERR_DID_TIME_OUT     DID_TIME_OUT
00100 #define SG_ERR_DID_BAD_TARGET   DID_BAD_TARGET
00101 #define SG_ERR_DID_ABORT        DID_ABORT
00102 #define SG_ERR_DID_PARITY       DID_PARITY
00103 #define SG_ERR_DID_ERROR        DID_ERROR
00104 #define SG_ERR_DID_RESET        DID_RESET
00105 #define SG_ERR_DID_BAD_INTR     DID_BAD_INTR
00106 #define SG_ERR_DID_PASSTHROUGH  DID_PASSTHROUGH
00107 #define SG_ERR_DID_SOFT_ERROR   DID_SOFT_ERROR
00108 
00109 /* The following are 'driver_status' codes */
00110 #ifndef DRIVER_OK
00111 #define DRIVER_OK 0x00
00112 #endif
00113 #ifndef DRIVER_BUSY
00114 #define DRIVER_BUSY 0x01
00115 #define DRIVER_SOFT 0x02
00116 #define DRIVER_MEDIA 0x03
00117 #define DRIVER_ERROR 0x04
00118 #define DRIVER_INVALID 0x05
00119 #define DRIVER_TIMEOUT 0x06
00120 #define DRIVER_HARD 0x07
00121 #define DRIVER_SENSE 0x08                         /* Sense_buffer has been set */
00122 
00123 /* Following "suggests" are "or-ed" with one of previous 8 entries */
00124 #define SUGGEST_RETRY 0x10
00125 #define SUGGEST_ABORT 0x20
00126 #define SUGGEST_REMAP 0x30
00127 #define SUGGEST_DIE 0x40
00128 #define SUGGEST_SENSE 0x80
00129 #define SUGGEST_IS_OK 0xff
00130 #endif
00131 #ifndef DRIVER_MASK
00132 #define DRIVER_MASK 0x0f
00133 #endif
00134 #ifndef SUGGEST_MASK
00135 #define SUGGEST_MASK 0xf0
00136 #endif
00137 
00138 /* These defines are to isolate applictaions from kernel define changes */
00139 #define SG_ERR_DRIVER_OK        DRIVER_OK
00140 #define SG_ERR_DRIVER_BUSY      DRIVER_BUSY
00141 #define SG_ERR_DRIVER_SOFT      DRIVER_SOFT
00142 #define SG_ERR_DRIVER_MEDIA     DRIVER_MEDIA
00143 #define SG_ERR_DRIVER_ERROR     DRIVER_ERROR
00144 #define SG_ERR_DRIVER_INVALID   DRIVER_INVALID
00145 #define SG_ERR_DRIVER_TIMEOUT   DRIVER_TIMEOUT
00146 #define SG_ERR_DRIVER_HARD      DRIVER_HARD
00147 #define SG_ERR_DRIVER_SENSE     DRIVER_SENSE
00148 #define SG_ERR_SUGGEST_RETRY    SUGGEST_RETRY
00149 #define SG_ERR_SUGGEST_ABORT    SUGGEST_ABORT
00150 #define SG_ERR_SUGGEST_REMAP    SUGGEST_REMAP
00151 #define SG_ERR_SUGGEST_DIE      SUGGEST_DIE
00152 #define SG_ERR_SUGGEST_SENSE    SUGGEST_SENSE
00153 #define SG_ERR_SUGGEST_IS_OK    SUGGEST_IS_OK
00154 #define SG_ERR_DRIVER_MASK      DRIVER_MASK
00155 #define SG_ERR_SUGGEST_MASK     SUGGEST_MASK
00156 
00157 /* The following "category" function returns one of the following */
00158 #define SG_ERR_CAT_CLEAN 0                        /* No errors or other information */
00159 #define SG_ERR_CAT_MEDIA_CHANGED 1                /* interpreted from sense buffer */
00160 #define SG_ERR_CAT_RESET 2                        /* interpreted from sense buffer */
00161 #define SG_ERR_CAT_TIMEOUT 3
00162 #define SG_ERR_CAT_RECOVERED 4                    /* Successful command after recovered err */
00163 #define SG_ERR_CAT_SENSE 98                       /* Something else is in the sense buffer */
00164 #define SG_ERR_CAT_OTHER 99                       /* Some other error/warning has occurred */
00165 
00166 typedef struct my_sg_scsi_id
00167 {
00168   int host_no;                                    /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
00169   int channel;
00170   int scsi_id;                                    /* scsi id of target device */
00171   int lun;
00172   int scsi_type;                                  /* TYPE_... defined in scsi/scsi.h */
00173   short h_cmd_per_lun;                            /* host (adapter) maximum commands per lun */
00174   short d_queue_depth;                            /* device (or adapter) maximum queue length */
00175   int unused1;                                    /* probably find a good use, set 0 for now */
00176   int unused2;                                    /* ditto */
00177 }
00178 
00179 
00180 My_sg_scsi_id;
00181 
00182 typedef struct my_scsi_idlun
00183 {
00184   int mux4;
00185   int host_unique_id;
00186 }
00187 
00188 
00189 My_scsi_idlun;
00190 
00191 static const char *devices[] =
00192 {
00193   "/dev/sd*[!0-9]",  /* don't look at partitions */
00194   "/dev/scd*",
00195   "/dev/sr*",
00196   "/dev/cd*",
00197   "/dev/dvd*",
00198   "/dev/st*",
00199   "/dev/nst*",
00200   "/dev/nosst*",
00201   "/dev/tape*",
00202   NULL
00203 };
00204 
00205 static map < string, string > sg_map;
00206 
00207 static string scsi_handle(unsigned int host,
00208 int channel = -1,
00209 int id = -1,
00210 int lun = -1)
00211 {
00212   char buffer[10];
00213   string result = "SCSI:";
00214 
00215   snprintf(buffer, sizeof(buffer), "%02d", host);
00216   result += string(buffer);
00217 
00218   if (channel < 0)
00219     return result;
00220 
00221   snprintf(buffer, sizeof(buffer), "%02d", channel);
00222   result += string(":") + string(buffer);
00223 
00224   if (id < 0)
00225     return result;
00226 
00227   snprintf(buffer, sizeof(buffer), "%02d", id);
00228   result += string(":") + string(buffer);
00229 
00230   if (lun < 0)
00231     return result;
00232 
00233   snprintf(buffer, sizeof(buffer), "%02d", lun);
00234   result += string(":") + string(buffer);
00235 
00236   return result;
00237 }
00238 
00239 
00240 static const char *scsi_type(int type)
00241 {
00242   switch (type)
00243   {
00244     case 0:
00245       return "Disk";
00246     case 1:
00247       return "Tape";
00248     case 3:
00249       return "Processor";
00250     case 4:
00251       return "Write-Once Read-Only Memory";
00252     case 5:
00253       return "CD-ROM";
00254     case 6:
00255       return "Scanner";
00256     case 7:
00257       return "Magneto-optical Disk";
00258     case 8:
00259       return "Medium Changer";
00260     case 0xd:
00261       return "Enclosure";
00262     default:
00263       return "";
00264   }
00265 }
00266 
00267 
00268 static int sg_err_category(int scsi_status,
00269 int host_status,
00270 int driver_status,
00271 const unsigned char *sense_buffer,
00272 int sb_len)
00273 {
00274   scsi_status &= 0x7e;
00275   if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status))
00276     return SG_ERR_CAT_CLEAN;
00277   if ((SCSI_CHECK_CONDITION == scsi_status) ||
00278     (SCSI_COMMAND_TERMINATED == scsi_status) ||
00279     (SG_ERR_DRIVER_SENSE == (0xf & driver_status)))
00280   {
00281     if (sense_buffer && (sb_len > 2))
00282     {
00283       int sense_key;
00284       unsigned char asc;
00285 
00286       if (sense_buffer[0] & 0x2)
00287       {
00288         sense_key = sense_buffer[1] & 0xf;
00289         asc = sense_buffer[2];
00290       }
00291       else
00292       {
00293         sense_key = sense_buffer[2] & 0xf;
00294         asc = (sb_len > 12) ? sense_buffer[12] : 0;
00295       }
00296 
00297       if (RECOVERED_ERROR == sense_key)
00298         return SG_ERR_CAT_RECOVERED;
00299       else if (UNIT_ATTENTION == sense_key)
00300       {
00301         if (0x28 == asc)
00302           return SG_ERR_CAT_MEDIA_CHANGED;
00303         if (0x29 == asc)
00304           return SG_ERR_CAT_RESET;
00305       }
00306     }
00307     return SG_ERR_CAT_SENSE;
00308   }
00309   if (0 != host_status)
00310   {
00311     if ((SG_ERR_DID_NO_CONNECT == host_status) ||
00312       (SG_ERR_DID_BUS_BUSY == host_status) ||
00313       (SG_ERR_DID_TIME_OUT == host_status))
00314       return SG_ERR_CAT_TIMEOUT;
00315   }
00316   if (0 != driver_status)
00317   {
00318     if (SG_ERR_DRIVER_TIMEOUT == driver_status)
00319       return SG_ERR_CAT_TIMEOUT;
00320   }
00321   return SG_ERR_CAT_OTHER;
00322 }
00323 
00324 
00325 static bool do_modesense(int sg_fd,
00326 int page,
00327 int page_code,
00328 void *resp,
00329 int mx_resp_len)
00330 {
00331   int res;
00332   unsigned char senseCmdBlk[SENSE_CMD_LEN] =
00333     { SENSE_CMD_CODE, 0, 0, 0, 0, 0 };
00334   unsigned char sense_b[SENSE_BUFF_LEN];
00335   sg_io_hdr_t io_hdr;
00336 
00337   page &= 0x3f;
00338   page_code &= 3;
00339 
00340   senseCmdBlk[2] = (unsigned char) ((page_code << 6) | page);
00341   senseCmdBlk[4] = (unsigned char) 0xff;
00342   memset(&io_hdr, 0, sizeof(io_hdr));
00343   memset(sense_b, 0, sizeof(sense_b));
00344   io_hdr.interface_id = 'S';
00345   io_hdr.cmd_len = sizeof(senseCmdBlk);
00346   io_hdr.mx_sb_len = sizeof(sense_b);
00347   io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
00348   io_hdr.dxfer_len = mx_resp_len;
00349   io_hdr.dxferp = resp;
00350   io_hdr.cmdp = senseCmdBlk;
00351   io_hdr.sbp = sense_b;
00352   io_hdr.timeout = 20000;                         /* 20 seconds */
00353 
00354   if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
00355     return false;
00356 
00357   res =
00358     sg_err_category(io_hdr.status, io_hdr.host_status, io_hdr.driver_status,
00359     (unsigned char*)io_hdr.sbp, io_hdr.sb_len_wr);
00360   switch (res)
00361   {
00362     case SG_ERR_CAT_CLEAN:
00363     case SG_ERR_CAT_RECOVERED:
00364       return true;
00365     default:
00366       return false;
00367   }
00368 
00369   return true;
00370 }
00371 
00372 
00373 static bool do_inq(int sg_fd,
00374 int cmddt,
00375 int evpd,
00376 unsigned int pg_op,
00377 void *resp,
00378 int mx_resp_len,
00379 int noisy)
00380 {
00381   int res;
00382   unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, 0, 0 };
00383   unsigned char sense_b[SENSE_BUFF_LEN];
00384   sg_io_hdr_t io_hdr;
00385 
00386   if (cmddt)
00387     inqCmdBlk[1] |= 2;
00388   if (evpd)
00389     inqCmdBlk[1] |= 1;
00390   inqCmdBlk[2] = (unsigned char) pg_op;
00391   inqCmdBlk[4] = (unsigned char) mx_resp_len;
00392   memset(&io_hdr, 0, sizeof(io_hdr));
00393   memset(sense_b, 0, sizeof(sense_b));
00394   io_hdr.interface_id = 'S';
00395   io_hdr.cmd_len = sizeof(inqCmdBlk);
00396   io_hdr.mx_sb_len = sizeof(sense_b);
00397   io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
00398   io_hdr.dxfer_len = mx_resp_len;
00399   io_hdr.dxferp = resp;
00400   io_hdr.cmdp = inqCmdBlk;
00401   io_hdr.sbp = sense_b;
00402   io_hdr.timeout = 20000;                         /* 20 seconds */
00403 
00404   if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
00405     return false;
00406 
00407   res =
00408     sg_err_category(io_hdr.status, io_hdr.host_status, io_hdr.driver_status,
00409     (unsigned char*)io_hdr.sbp, io_hdr.sb_len_wr);
00410   switch (res)
00411   {
00412     case SG_ERR_CAT_CLEAN:
00413     case SG_ERR_CAT_RECOVERED:
00414       return true;
00415     default:
00416       return false;
00417   }
00418 
00419   return true;
00420 }
00421 
00422 
00423 static unsigned long decode_3_bytes(void *ptr)
00424 {
00425   unsigned char *p = (unsigned char *) ptr;
00426 
00427   return (p[0] << 16) + (p[1] << 8) + p[2];
00428 }
00429 
00430 
00431 static u_int16_t decode_word(void *ptr)
00432 {
00433   unsigned char *p = (unsigned char *) ptr;
00434 
00435   return (p[0] << 8) + p[1];
00436 }
00437 
00438 
00439 static bool do_inquiry(int sg_fd,
00440 hwNode & node)
00441 {
00442   char rsp_buff[MX_ALLOC_LEN + 1];
00443   int k;
00444   unsigned int len;
00445 
00446   if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
00447     return false;
00448 
00449   memset(rsp_buff, 0, sizeof(rsp_buff));
00450   if (!do_inq(sg_fd, 0, 0, 0, rsp_buff, 36, 1))
00451     return false;
00452 
00453   len = (unsigned int) rsp_buff[4] + 5;
00454 
00455   if ((len > 36) && (len < 256))
00456   {
00457     memset(rsp_buff, 0, sizeof(rsp_buff));
00458     if (!do_inq(sg_fd, 0, 0, 0, rsp_buff, len, 1))
00459       return false;
00460   }
00461   else
00462     return false;
00463 
00464   if (len != ((unsigned int) rsp_buff[4] + 5))
00465     return false;                                 // twin INQUIRYs yield different lengths
00466 
00467   unsigned ansiversion = rsp_buff[2] & 0x7;
00468 
00469   if (rsp_buff[1] & 0x80)
00470     node.addCapability("removable", "support is removable");
00471 
00472   node.setVendor(string(rsp_buff + 8, 8));
00473   if (len > 16)
00474     node.setProduct(string(rsp_buff + 16, 16));
00475   if (len > 32)
00476     node.setVersion(string(rsp_buff + 32, 4));
00477 
00478   if (ansiversion)
00479     node.setConfig("ansiversion", tostring(ansiversion));
00480 
00481   memset(rsp_buff, 0, sizeof(rsp_buff));
00482   if (do_inq(sg_fd, 0, 1, 0x80, rsp_buff, MX_ALLOC_LEN, 0))
00483   {
00484     len = rsp_buff[3];
00485     if (len > 0)
00486       node.setSerial(hw::strip(string(rsp_buff + 4, len)));
00487   }
00488 
00489   memset(rsp_buff, 0, sizeof(rsp_buff));
00490   if (do_modesense(sg_fd, 0x3F, 0, rsp_buff, sizeof(rsp_buff)))
00491   {
00492     unsigned long long sectsize = 0;
00493     unsigned long long heads = 0;
00494     unsigned long long cyl = 0;
00495     unsigned long long sectors = 0;
00496     unsigned long rpm = 0;
00497     u_int8_t *end = (u_int8_t *) rsp_buff + (u_int8_t) rsp_buff[0];
00498     u_int8_t *p = NULL;
00499 
00500     if (rsp_buff[3] == 8)
00501       sectsize = decode_3_bytes(rsp_buff + 9);
00502 
00503     p = (u_int8_t *) & rsp_buff[4];
00504     p += (u_int8_t) rsp_buff[3];
00505     while (p < end)
00506     {
00507       u_int8_t page = *p & 0x3F;
00508 
00509       if (page == 3)
00510       {
00511         sectors = decode_word(p + 10);
00512         sectsize = decode_word(p + 12);
00513       }
00514       if (page == 4)
00515       {
00516         cyl = decode_3_bytes(p + 2);
00517         rpm = decode_word(p + 20);
00518         heads = p[5];
00519       }
00520 
00521       p += p[1] + 2;
00522     }
00523 
00524     node.setCapacity(heads * cyl * sectors * sectsize);
00525 
00526     if (rpm / 15000 == 1)
00527       node.addCapability("15000rpm", "15000 rotations per minute");
00528     else
00529     {
00530       if (rpm / 10000 == 1)
00531         node.addCapability("10000rpm", "10000 rotations per minute");
00532       else
00533       {
00534         if (rpm / 7200 == 1)
00535           node.addCapability("7200rpm", "7200 rotations per minute");
00536         else
00537         {
00538           if (rpm / 5400 == 1)
00539             node.addCapability("5400rpm", "5400 rotations per minute");
00540         }
00541       }
00542     }
00543   }
00544 
00545   return true;
00546 }
00547 
00548 static void scan_devices()
00549 {
00550   int fd = -1;
00551   int i = 0;
00552   size_t j = 0;
00553   My_scsi_idlun m_idlun;
00554 
00555   for (i = 0; devices[i] != NULL; i++)
00556   {
00557     glob_t entries;
00558 
00559     if(glob(devices[i], 0, NULL, &entries) == 0)
00560     {
00561       for(j=0; j < entries.gl_pathc; j++)
00562       {
00563         fd = open(entries.gl_pathv[j], O_RDONLY | O_NONBLOCK);
00564         if (fd >= 0)
00565         {
00566           int bus = -1;
00567           union
00568           {
00569             char host[50];
00570             int length;
00571           } tmp;
00572           tmp.length = sizeof(tmp.host);
00573           memset(tmp.host, 0, sizeof(tmp.host));
00574 
00575           if(ioctl(fd, SCSI_IOCTL_PROBE_HOST, &tmp.length) >= 0)
00576           {
00577             if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus) >= 0)
00578             {
00579               memset(&m_idlun, 0, sizeof(m_idlun));
00580               if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun) >= 0)
00581               {
00582                 sg_map[string(entries.gl_pathv[j])] = scsi_handle(bus, (m_idlun.mux4 >> 16) & 0xff,
00583                   m_idlun.mux4 & 0xff,
00584                   (m_idlun.mux4 >> 8) & 0xff);
00585               }
00586             }
00587           }
00588           close(fd);
00589         }
00590       }
00591       globfree(&entries);
00592     }
00593   }
00594 }
00595 
00596 
00597 static void find_logicalname(hwNode & n)
00598 {
00599   map < string, string >::iterator i = sg_map.begin();
00600 
00601   for (i = sg_map.begin(); i != sg_map.end(); i++)
00602   {
00603     if (i->second == n.getHandle())
00604     {
00605       n.setLogicalName(i->first);
00606       n.claim();
00607     }
00608   }
00609 }
00610 
00611 
00612 static string scsi_businfo(int host,
00613 int channel = -1,
00614 int target = -1,
00615 int lun = -1)
00616 {
00617   string result;
00618 
00619   result = "scsi@" + tostring(host);
00620 
00621   if (channel >= 0)
00622   {
00623     result += ":" + tostring(channel);
00624 
00625     if (target >= 0)
00626     {
00627       result += "." + tostring(target);
00628 
00629       if (lun >= 0)
00630         result += "." + tostring(lun);
00631     }
00632   }
00633 
00634   return result;
00635 }
00636 
00637 
00638 static string host_logicalname(int i)
00639 {
00640   return "scsi"+tostring(i);
00641 }
00642 
00643 
00644 static string host_kname(int i)                   // for sysfs
00645 {
00646   return "host"+tostring(i);
00647 }
00648 
00649 
00650 static bool atapi(const hwNode & n)
00651 {
00652   return n.isCapable("atapi") && (n.countChildren() == 0);
00653 }
00654 
00655 
00656 static bool scan_sg(int sg,
00657 hwNode & n)
00658 {
00659   char buffer[20];
00660   int fd = -1;
00661   My_sg_scsi_id m_id;
00662   char slot_name[64];                             // should be 16 but some 2.6 kernels require 32 bytes
00663   string host = "";
00664   string businfo = "";
00665   hwNode *parent = NULL;
00666   int emulated = 0;
00667   bool ghostdeventry = false;
00668 
00669   snprintf(buffer, sizeof(buffer), SG_X, sg);
00670 
00671   ghostdeventry = !exists(buffer);
00672 
00673   if(ghostdeventry) mknod(buffer, (S_IFCHR | S_IREAD), MKDEV(SG_MAJOR, sg));
00674   fd = open(buffer, OPEN_FLAG | O_NONBLOCK);
00675   if(ghostdeventry) unlink(buffer);
00676   if (fd < 0)
00677     return false;
00678 
00679   memset(&m_id, 0, sizeof(m_id));
00680   if (ioctl(fd, SG_GET_SCSI_ID, &m_id) < 0)
00681   {
00682     close(fd);
00683     return true;                                  // we failed to get info but still hope we can continue
00684   }
00685 
00686   emulated = 0;
00687   ioctl(fd, SG_EMULATED_HOST, &emulated);
00688 
00689   host = host_logicalname(m_id.host_no);
00690   businfo = scsi_businfo(m_id.host_no);
00691 
00692   hwNode device = hwNode("generic");
00693 
00694   switch (m_id.scsi_type)
00695   {
00696     case 0:
00697     case 14:
00698       device = hwNode("disk", hw::disk);
00699       break;
00700     case 1:
00701       device = hwNode("tape", hw::tape);
00702       break;
00703     case 3:
00704       device = hwNode("processor", hw::processor);
00705       break;
00706     case 4:
00707     case 5:
00708       device = hwNode("cdrom", hw::disk);
00709       break;
00710     case 6:
00711       device = hwNode("scanner", hw::generic);
00712       break;
00713     case 7:
00714       device = hwNode("magnetooptical", hw::disk);
00715       break;
00716     case 8:
00717       device = hwNode("changer", hw::generic);
00718       break;
00719     case 0xd:
00720       device = hwNode("enclosure", hw::generic);
00721       break;
00722   }
00723 
00724   device.setDescription(string(scsi_type(m_id.scsi_type)));
00725   device.setHandle(scsi_handle(m_id.host_no,
00726     m_id.channel, m_id.scsi_id, m_id.lun));
00727   device.setBusInfo(scsi_businfo(m_id.host_no,
00728     m_id.channel, m_id.scsi_id, m_id.lun));
00729   device.setPhysId(m_id.channel, m_id.scsi_id, m_id.lun);
00730   find_logicalname(device);
00731   do_inquiry(fd, device);
00732   if(device.getVendor() == "ATA")
00733   {
00734     device.setDescription("ATA " + device.getDescription());
00735     device.setVendor("");
00736   }
00737   else
00738   {
00739     device.setDescription("SCSI " + device.getDescription());
00740     device.addHint("bus.icon", string("scsi"));
00741   }
00742   if ((m_id.scsi_type == 4) || (m_id.scsi_type == 5))
00743     scan_cdrom(device);
00744   if ((m_id.scsi_type == 0) || (m_id.scsi_type == 7) || (m_id.scsi_type == 14))
00745     scan_disk(device);
00746 
00747   memset(slot_name, 0, sizeof(slot_name));
00748   if (ioctl(fd, SCSI_IOCTL_GET_PCI, slot_name) >= 0)
00749   {
00750     parent = n.findChildByBusInfo(guessBusInfo(hw::strip(slot_name)));
00751   }
00752 
00753   if (!parent)
00754     parent = n.findChildByLogicalName(host);
00755 
00756                                                   // IDE-SCSI pseudo host controller
00757   if (emulated && device.getConfig("driver")=="ide-scsi")
00758   {
00759     hwNode *ideatapi = n.findChild(atapi);
00760 
00761     if (ideatapi)
00762       parent = ideatapi->addChild(hwNode("scsi", hw::storage));
00763   }
00764 
00765   if (!parent)
00766   {
00767     hwNode *core = n.getChild("core");
00768 
00769     if (core)
00770       parent = core->addChild(hwNode("scsi", hw::storage));
00771   }
00772 
00773   if (!parent)
00774     parent = n.addChild(hwNode("scsi", hw::storage));
00775 
00776   if (!parent)
00777   {
00778     close(fd);
00779     return true;
00780   }
00781 
00782   if(parent->getBusInfo() == "")
00783     parent->setBusInfo(guessBusInfo(hw::strip(slot_name)));
00784   parent->setLogicalName(host);
00785   parent->claim();
00786 
00787   if (emulated)
00788   {
00789     parent->addCapability("emulated", "Emulated device");
00790   }
00791   parent->addChild(device);
00792 
00793   close(fd);
00794 
00795   return true;
00796 }
00797 
00798 
00799 static bool scan_hosts(hwNode & node)
00800 {
00801   struct dirent **namelist = NULL;
00802   int n;
00803   vector < string > host_strs;
00804 
00805   if (!pushd("/proc/scsi"))
00806     return false;
00807   n = scandir(".", &namelist, selectdir, alphasort);
00808   popd();
00809   if ((n < 0) || !namelist)
00810     return false;
00811 
00812   pushd("/proc/scsi");
00813   for (int i = 0; i < n; i++)
00814   {
00815     struct dirent **filelist = NULL;
00816     int m = 0;
00817 
00818     pushd(namelist[i]->d_name);
00819     m = scandir(".", &filelist, NULL, alphasort);
00820     popd();
00821 
00822     if (m >= 0)
00823     {
00824       for (int j = 0; j < m; j++)
00825       {
00826         char *end = NULL;
00827         int number = -1;
00828 
00829         number = strtol(filelist[j]->d_name, &end, 0);
00830 
00831         if ((number >= 0) && (end != filelist[j]->d_name))
00832         {
00833           hwNode *controller =
00834             node.findChildByLogicalName(host_logicalname(number));
00835 
00836           if (!controller)
00837           {
00838             string parentbusinfo = sysfs_getbusinfo(sysfs::entry::byClass("scsi_host", host_kname(number)));
00839 
00840             controller = node.findChildByBusInfo(parentbusinfo);
00841           }
00842 
00843           if (!controller)
00844           {
00845             controller = node.addChild(hwNode("scsi", hw::storage));
00846             if (controller)
00847             {
00848               controller->setLogicalName(host_logicalname(number));
00849               controller->setBusInfo(scsi_businfo(number));
00850             }
00851           }
00852 
00853           if (controller)
00854           {
00855             controller->setLogicalName(host_logicalname(number));
00856             controller->setConfig(string("driver"),
00857               string(namelist[i]->d_name));
00858             controller->setHandle(scsi_handle(number));
00859             controller->addCapability("scsi-host", "SCSI host adapter");
00860             controller->claim();
00861           }
00862         }
00863         free(filelist[j]);
00864       }
00865       free(filelist);
00866     }
00867     free(namelist[i]);
00868   }
00869   free(namelist);
00870   popd();
00871 
00872   if (!loadfile("/proc/scsi/sg/host_strs", host_strs))
00873     return false;
00874 
00875   for (unsigned int i = 0; i < host_strs.size(); i++)
00876   {
00877     hwNode *host = node.findChildByLogicalName(host_logicalname(i));
00878 
00879     if (host)
00880     {
00881       if ((host->getProduct() == "") && (host->getDescription() == ""))
00882         host->setDescription("SCSI storage controller");
00883     }
00884   }
00885 
00886   return true;
00887 }
00888 
00889 
00890 bool scan_scsi(hwNode & n)
00891 {
00892   int i = 0;
00893 
00894   scan_devices();
00895 
00896   while (scan_sg(i, n))
00897     i++;
00898 
00899   scan_hosts(n);
00900 
00901   return false;
00902 }