Back to index

lshw  02.16
ide.cc
Go to the documentation of this file.
00001 /*
00002  *  ide.cc
00003  *
00004  *  This scan tries to detect all IDE interfaces in the system by scanning
00005  *  /proc/ide and looking for /proc/ide/xxx/channel
00006  *  It then tries to find the parent device for this interface (on 2.4 kernels,
00007  *  /proc/ide/xxx/identify contains useful information for PCI devices), other-
00008  *  wise, guessParent() is used.
00009  *  Each IDE-connected device is scanned and more information is gathered
00010  *  by calling scan_disk() and scan_cdrom(), as appropriate.
00011  */
00012 
00013 #include "version.h"
00014 #include "cpuinfo.h"
00015 #include "osutils.h"
00016 #include "cdrom.h"
00017 #include "disk.h"
00018 #include "heuristics.h"
00019 #include <sys/types.h>
00020 #include <sys/ioctl.h>
00021 #include <sys/stat.h>
00022 #include <endian.h>
00023 #include <fcntl.h>
00024 #include <unistd.h>
00025 #include <stdio.h>
00026 #include <string.h>
00027 #include <stdlib.h>
00028 #include <dirent.h>
00029 #include <ctype.h>
00030 #include <vector>
00031 #include <linux/hdreg.h>
00032 
00033 __ID("@(#) $Id: ide.cc 2433 2012-01-10 22:01:30Z lyonel $");
00034 
00035 #define PROC_IDE "/proc/ide"
00036 
00037 #define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
00038 #define PCI_FUNC(devfn)         ((devfn) & 0x07)
00039 
00040 #define IORDY_SUP               0x0800            /* 1=support; 0=may be supported */
00041 #define IORDY_OFF               0x0400            /* 1=may be disabled */
00042 #define LBA_SUP                 0x0200            /* 1=Logical Block Address support */
00043 #define DMA_SUP                 0x0100            /* 1=Direct Memory Access support */
00044 #define DMA_IL_SUP              0x8000            /* 1=interleaved DMA support (ATAPI) */
00045 #define CMD_Q_SUP               0x4000            /* 1=command queuing support (ATAPI) */
00046 #define OVLP_SUP                0x2000            /* 1=overlap operation support (ATAPI) */
00047 
00048 #define GEN_CONFIG              0                 /* general configuration */
00049 #define CD_ROM                  5
00050 #define NOT_ATA                 0x8000
00051 #define NOT_ATAPI               0x4000            /* (check only if bit 15 == 1) */
00052 #define EQPT_TYPE               0x1f00
00053 
00054 static const char *description[] =
00055 {
00056   "Direct-access device",                         /* word 0, bits 12-8 = 00 */
00057   "Sequential-access device",                     /* word 0, bits 12-8 = 01 */
00058   "Printer",                                      /* word 0, bits 12-8 = 02 */
00059   "Processor",                                    /* word 0, bits 12-8 = 03 */
00060   "Write-once device",                            /* word 0, bits 12-8 = 04 */
00061   "CD-ROM",                                       /* word 0, bits 12-8 = 05 */
00062   "Scanner",                                      /* word 0, bits 12-8 = 06 */
00063   "Optical memory",                               /* word 0, bits 12-8 = 07 */
00064   "Medium changer",                               /* word 0, bits 12-8 = 08 */
00065   "Communications device",                        /* word 0, bits 12-8 = 09 */
00066   "ACS-IT8 device",                               /* word 0, bits 12-8 = 0a */
00067   "ACS-IT8 device",                               /* word 0, bits 12-8 = 0b */
00068   "Array controller",                             /* word 0, bits 12-8 = 0c */
00069   "Enclosure services",                           /* word 0, bits 12-8 = 0d */
00070   "Reduced block command device",                 /* word 0, bits 12-8 = 0e */
00071   "Optical card reader/writer",                   /* word 0, bits 12-8 = 0f */
00072   "",                                             /* word 0, bits 12-8 = 10 */
00073   "",                                             /* word 0, bits 12-8 = 11 */
00074   "",                                             /* word 0, bits 12-8 = 12 */
00075   "",                                             /* word 0, bits 12-8 = 13 */
00076   "",                                             /* word 0, bits 12-8 = 14 */
00077   "",                                             /* word 0, bits 12-8 = 15 */
00078   "",                                             /* word 0, bits 12-8 = 16 */
00079   "",                                             /* word 0, bits 12-8 = 17 */
00080   "",                                             /* word 0, bits 12-8 = 18 */
00081   "",                                             /* word 0, bits 12-8 = 19 */
00082   "",                                             /* word 0, bits 12-8 = 1a */
00083   "",                                             /* word 0, bits 12-8 = 1b */
00084   "",                                             /* word 0, bits 12-8 = 1c */
00085   "",                                             /* word 0, bits 12-8 = 1d */
00086   "",                                             /* word 0, bits 12-8 = 1e */
00087   "Unknown",                                      /* word 0, bits 12-8 = 1f */
00088 };
00089 
00090 /* older kernels (2.2.x) have incomplete id structure for IDE devices */
00091 #ifndef __NEW_HD_DRIVE_ID
00092 #define command_set_1 command_sets
00093 #define command_set_2 word83
00094 #define hw_config word93
00095 #endif
00096 
00097 static unsigned long long get_longlong(const string & path)
00098 {
00099   FILE *in = fopen(path.c_str(), "r");
00100   unsigned long long l = 0;
00101 
00102   if (in)
00103   {
00104     if(fscanf(in, "%lld", &l) < 1)
00105       l = 0;
00106     fclose(in);
00107   }
00108 
00109   return l;
00110 }
00111 
00112 
00113 static string get_pciid(const string & bus,
00114 const string & device)
00115 {
00116   char buffer[20];
00117   int pcibus, pcidevfunc;
00118 
00119   sscanf(bus.c_str(), "%x", &pcibus);
00120   sscanf(device.c_str(), "%x", &pcidevfunc);
00121   snprintf(buffer, sizeof(buffer), "%02x:%02x.%x", pcibus,
00122     PCI_SLOT(pcidevfunc), PCI_FUNC(pcidevfunc));
00123 
00124   return string(buffer);
00125 }
00126 
00127 
00128 static bool probe_ide(const string & name,
00129 hwNode & device)
00130 {
00131   struct hd_driveid id;
00132   const u_int8_t *id_regs = (const u_int8_t *) &id;
00133   int fd = open(device.getLogicalName().c_str(), O_RDONLY | O_NONBLOCK);
00134 
00135   if (fd < 0)
00136     return false;
00137 
00138   memset(&id, 0, sizeof(id));
00139   if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0)
00140   {
00141     close(fd);
00142     return false;
00143   }
00144 
00145   u_int8_t args[4 + 512] = { WIN_IDENTIFY, 0, 0, 1, };
00146 
00147   if(id.config & 0x8000)                          // we have a packet device
00148   {
00149     args[0] = WIN_PIDENTIFY;                      // so use the right command to avoid kernel messages (Aborted Command)
00150     device.addCapability("packet", "ATAPI packet device");
00151   }
00152 
00153   if (ioctl(fd, HDIO_DRIVE_CMD, &args) != 0)
00154   {
00155     close(fd);
00156     return false;
00157   }
00158 
00159   close(fd);
00160 
00161   u_int16_t pidentity[256];
00162   for (int i = 0; i < 256; i++)
00163     pidentity[i] = args[4 + 2 * i] + (args[4 + 2 * i + 1] << 8);
00164 
00165   if (id.model[0])
00166     device.setProduct(hw::strip(string((char *) id.model, sizeof(id.model))));
00167   if (id.fw_rev[0])
00168     device.
00169       setVersion(hw::strip(string((char *) id.fw_rev, sizeof(id.fw_rev))));
00170   if (id.serial_no[0])
00171     device.
00172       setSerial(hw::
00173       strip(string((char *) id.serial_no, sizeof(id.serial_no))));
00174 
00175   if (!(pidentity[GEN_CONFIG] & NOT_ATA))
00176   {
00177     device.addCapability("ata", "ATA");
00178     device.setDescription("ATA Disk");
00179   }
00180   else if (!(pidentity[GEN_CONFIG] & NOT_ATAPI))
00181   {
00182     u_int8_t eqpt = (pidentity[GEN_CONFIG] & EQPT_TYPE) >> 8;
00183     device.addCapability("atapi", "ATAPI");
00184 
00185     if (eqpt == CD_ROM)
00186       device.addCapability("cdrom", "can read CD-ROMs");
00187 
00188     if (eqpt < 0x20)
00189       device.setDescription("IDE " + string(description[eqpt]));
00190   }
00191   if (id.config & (1 << 7))
00192     device.addCapability("removable", "support is removable");
00193   if (id.config & (1 << 15))
00194     device.addCapability("nonmagnetic", "support is non-magnetic (optical)");
00195   if (id.capability & 1)
00196     device.addCapability("dma", "Direct Memory Access");
00197   if (id.capability & 2)
00198     device.addCapability("lba", "Large Block Addressing");
00199   if (id.capability & 8)
00200     device.addCapability("iordy", "I/O ready reporting");
00201   if (id.command_set_1 & 1)
00202     device.addCapability("smart",
00203       "S.M.A.R.T. (Self-Monitoring And Reporting Technology)");
00204   if (id.command_set_1 & 2)
00205     device.addCapability("security", "ATA security extensions");
00206   if (id.command_set_1 & 4)
00207     device.addCapability("removable", "support is removable");
00208   if (id.command_set_1 & 8)
00209     device.addCapability("pm", "Power Management");
00210   if (id.command_set_2 & 8)
00211     device.addCapability("apm", "Advanced Power Management");
00212 
00213   if ((id.capability & 8) || (id.field_valid & 2))
00214   {
00215     if (id.field_valid & 4)
00216     {
00217       if (id.dma_ultra & 0x100)
00218         device.setConfig("mode", "udma0");
00219       if (id.dma_ultra & 0x200)
00220         device.setConfig("mode", "udma1");
00221       if (id.dma_ultra & 0x400)
00222         device.setConfig("mode", "udma2");
00223       if (id.hw_config & 0x2000)
00224       {
00225         if (id.dma_ultra & 0x800)
00226           device.setConfig("mode", "udma3");
00227         if (id.dma_ultra & 0x1000)
00228           device.setConfig("mode", "udma4");
00229         if (id.dma_ultra & 0x2000)
00230           device.setConfig("mode", "udma5");
00231         if (id.dma_ultra & 0x4000)
00232           device.setConfig("mode", "udma6");
00233         if (id.dma_ultra & 0x8000)
00234           device.setConfig("mode", "udma7");
00235       }
00236     }
00237   }
00238 
00239   if (id_regs[83] & 8)
00240     device.addCapability("apm", "Advanced Power Management");
00241 
00242 //if (device.isCapable("iordy") && (id.capability & 4))
00243 //device.setConfig("iordy", "yes");
00244 
00245   if (device.isCapable("smart"))
00246   {
00247     if (id.command_set_2 & (1 << 14))
00248       device.setConfig("smart", "on");
00249     else
00250       device.setConfig("smart", "off");
00251   }
00252 
00253   if (device.isCapable("apm"))
00254   {
00255     if (!(id_regs[86] & 8))
00256       device.setConfig("apm", "off");
00257     else
00258       device.setConfig("apm", "on");
00259   }
00260 
00261   if (device.isCapable("lba"))
00262     device.setCapacity((unsigned long long) id.lba_capacity * 512);
00263   if (device.isCapable("removable"))
00264     device.setCapacity(0);                        // we'll first have to make sure we have a disk
00265 
00266   if (device.isCapable("cdrom"))
00267     scan_cdrom(device);
00268   else
00269     scan_disk(device);
00270 
00271 #if 0
00272   if (!(iddata[GEN_CONFIG] & NOT_ATA))
00273     device.addCapability("ata");
00274   else if (iddata[GEN_CONFIG] == CFA_SUPPORT_VAL)
00275   {
00276     device.addCapability("ata");
00277     device.addCapability("compactflash");
00278   }
00279   else if (!(iddata[GEN_CONFIG] & NOT_ATAPI))
00280   {
00281     device.addCapability("atapi");
00282     device.setDescription(description[(iddata[GEN_CONFIG] & EQPT_TYPE) >> 8]);
00283   }
00284 
00285   if (iddata[START_MODEL])
00286     device.setProduct(print_ascii((char *) &iddata[START_MODEL],
00287       LENGTH_MODEL));
00288   if (iddata[START_SERIAL])
00289     device.
00290       setSerial(print_ascii((char *) &iddata[START_SERIAL], LENGTH_SERIAL));
00291   if (iddata[START_FW_REV])
00292     device.
00293       setVersion(print_ascii((char *) &iddata[START_FW_REV], LENGTH_FW_REV));
00294   if (iddata[CAPAB_0] & LBA_SUP)
00295     device.addCapability("lba");
00296   if (iddata[CAPAB_0] & IORDY_SUP)
00297     device.addCapability("iordy");
00298   if (iddata[CAPAB_0] & IORDY_OFF)
00299   {
00300     device.addCapability("iordy");
00301     device.addCapability("iordyoff");
00302   }
00303   if (iddata[CAPAB_0] & DMA_SUP)
00304     device.addCapability("dma");
00305   if (iddata[CAPAB_0] & DMA_IL_SUP)
00306   {
00307     device.addCapability("interleaved_dma");
00308     device.addCapability("dma");
00309   }
00310   if (iddata[CAPAB_0] & CMD_Q_SUP)
00311     device.addCapability("command_queuing");
00312   if (iddata[CAPAB_0] & OVLP_SUP)
00313     device.addCapability("overlap_operation");
00314 #endif
00315   return true;
00316 }
00317 
00318 
00319 static bool is_master(const string & device)
00320 {
00321   if (device == "")
00322     return false;
00323 
00324   switch ((device[device.length() - 1] - 'a') % 2)
00325   {
00326     case 0:
00327       return true;
00328     case 1:
00329       return false;
00330     default:
00331       return false;
00332   }
00333 }
00334 
00335 bool scan_ide(hwNode & n)
00336 {
00337   struct dirent **namelist;
00338   int nentries;
00339 
00340   pushd(PROC_IDE);
00341   nentries = scandir(".", &namelist, selectdir, alphasort);
00342   popd();
00343 
00344   if (nentries < 0)
00345     return false;
00346 
00347   for (int i = 0; i < nentries; i++)
00348   {
00349     vector < string > config;
00350     hwNode ide("ide",
00351       hw::bus);
00352 
00353     ide.setLogicalName(namelist[i]->d_name);
00354     ide.setHandle("IDE:" + string(namelist[i]->d_name));
00355 
00356     if (exists(string(PROC_IDE"/") + namelist[i]->d_name + "/channel"))
00357     {
00358       vector < string > identify;
00359       string channel = "";
00360       char *id = namelist[i]->d_name;
00361 
00362       while ((*id != 0) && (!isdigit(*id)))
00363         id++;
00364       if (*id != 0)
00365       {
00366         ide.setBusInfo("ide@" + string(id));
00367         ide.setPhysId(string(id));
00368       }
00369 
00370       loadfile(string(PROC_IDE"/") + namelist[i]->d_name + "/config", config);
00371       if (config.size() > 0)
00372         splitlines(config[0], identify, ' ');
00373       config.clear();
00374       loadfile(string(PROC_IDE"/") + namelist[i]->d_name + "/channel", config);
00375       if (config.size() > 0)
00376         channel = config[0];
00377       config.clear();
00378 
00379 //if (identify.size() >= 1)
00380       {
00381         struct dirent **devicelist;
00382         int ndevices;
00383 
00384         pushd(string(PROC_IDE) + "/" + namelist[i]->d_name);
00385         ndevices = scandir(".", &devicelist, selectdir, alphasort);
00386         popd();
00387 
00388         for (int j = 0; j < ndevices; j++)
00389         {
00390           string basepath =
00391             string(PROC_IDE) + "/" + namelist[i]->d_name + "/" +
00392             devicelist[j]->d_name;
00393           hwNode idedevice("device",
00394             hw::storage);
00395 
00396           idedevice =
00397             hwNode(get_string(basepath + "/media", "disk"), hw::disk);
00398 
00399           idedevice.setCapacity(512 * get_longlong(basepath + "/capacity"));
00400           idedevice.setLogicalName(string("/dev/") + devicelist[j]->d_name);
00401           idedevice.setProduct(get_string(basepath + "/model"));
00402           idedevice.claim();
00403           idedevice.setHandle(ide.getHandle() + ":" +
00404             string(devicelist[j]->d_name));
00405           if (is_master(devicelist[j]->d_name))
00406             idedevice.setPhysId(0);
00407           else
00408             idedevice.setPhysId(1);
00409           idedevice.setBusInfo(ide.getBusInfo() + "." +
00410             idedevice.getPhysId());
00411 
00412           probe_ide(devicelist[j]->d_name, idedevice);
00413 
00414           ide.addChild(idedevice);
00415           free(devicelist[j]);
00416         }
00417         free(devicelist);
00418 
00419         ide.setDescription(hw::strip("IDE Channel " + hw::strip(channel)));
00420 
00421         if ( identify.size() == 11 && identify[0] == "pci")
00422         {
00423           string businfo = guessBusInfo(get_pciid(identify[2], identify[4]));
00424           hwNode *parent = n.findChildByBusInfo(businfo);
00425 
00426           if (parent)
00427           {
00428             parent->claim();
00429             ide.setClock(parent->getClock());
00430             parent->addChild(ide);
00431           }
00432         }
00433         else                                      // we have to guess the parent device
00434         {
00435           hwNode * parent = guessParent(ide, n);
00436           if(parent)
00437           {
00438             parent->claim();
00439             ide.setClock(parent->getClock());
00440             parent->addChild(ide);
00441           }
00442           else
00443             for (unsigned int k = 0; k < ide.countChildren(); k++)
00444           {
00445             hwNode *candidate =
00446               n.findChildByLogicalName(ide.getChild(k)->getLogicalName());
00447 
00448             if (candidate)
00449             {
00450               parent = candidate;
00451               candidate->merge(*ide.getChild(k));
00452               break;
00453             }
00454           }
00455           if(!parent) n.addChild(ide);
00456         }
00457       }
00458 
00459     }
00460 
00461     free(namelist[i]);
00462   }
00463   free(namelist);
00464 
00465   return false;
00466 }