/ src / core / ide.cc
ide.cc
  1  /*
  2   *  ide.cc
  3   *
  4   *  This scan tries to detect all IDE interfaces in the system by scanning
  5   *  /proc/ide and looking for /proc/ide/xxx/channel
  6   *  It then tries to find the parent device for this interface (on 2.4 kernels,
  7   *  /proc/ide/xxx/identify contains useful information for PCI devices), other-
  8   *  wise, guessParent() is used.
  9   *  Each IDE-connected device is scanned and more information is gathered
 10   *  by calling scan_disk() and scan_cdrom(), as appropriate.
 11   */
 12  
 13  #include "version.h"
 14  #include "cpuinfo.h"
 15  #include "osutils.h"
 16  #include "cdrom.h"
 17  #include "disk.h"
 18  #include "heuristics.h"
 19  #include <sys/types.h>
 20  #include <sys/ioctl.h>
 21  #include <sys/stat.h>
 22  #include <endian.h>
 23  #include <fcntl.h>
 24  #include <unistd.h>
 25  #include <stdio.h>
 26  #include <string.h>
 27  #include <stdlib.h>
 28  #include <dirent.h>
 29  #include <ctype.h>
 30  #include <vector>
 31  #include <linux/hdreg.h>
 32  
 33  __ID("@(#) $Id$");
 34  
 35  #define PROC_IDE "/proc/ide"
 36  
 37  #define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
 38  #define PCI_FUNC(devfn)         ((devfn) & 0x07)
 39  
 40  #define IORDY_SUP               0x0800            /* 1=support; 0=may be supported */
 41  #define IORDY_OFF               0x0400            /* 1=may be disabled */
 42  #define LBA_SUP                 0x0200            /* 1=Logical Block Address support */
 43  #define DMA_SUP                 0x0100            /* 1=Direct Memory Access support */
 44  #define DMA_IL_SUP              0x8000            /* 1=interleaved DMA support (ATAPI) */
 45  #define CMD_Q_SUP               0x4000            /* 1=command queuing support (ATAPI) */
 46  #define OVLP_SUP                0x2000            /* 1=overlap operation support (ATAPI) */
 47  
 48  #define GEN_CONFIG              0                 /* general configuration */
 49  #define CD_ROM                  5
 50  #define NOT_ATA                 0x8000
 51  #define NOT_ATAPI               0x4000            /* (check only if bit 15 == 1) */
 52  #define EQPT_TYPE               0x1f00
 53  
 54  static const char *description[] =
 55  {
 56    "Direct-access device",                         /* word 0, bits 12-8 = 00 */
 57    "Sequential-access device",                     /* word 0, bits 12-8 = 01 */
 58    "Printer",                                      /* word 0, bits 12-8 = 02 */
 59    "Processor",                                    /* word 0, bits 12-8 = 03 */
 60    "Write-once device",                            /* word 0, bits 12-8 = 04 */
 61    "CD-ROM",                                       /* word 0, bits 12-8 = 05 */
 62    "Scanner",                                      /* word 0, bits 12-8 = 06 */
 63    "Optical memory",                               /* word 0, bits 12-8 = 07 */
 64    "Medium changer",                               /* word 0, bits 12-8 = 08 */
 65    "Communications device",                        /* word 0, bits 12-8 = 09 */
 66    "ACS-IT8 device",                               /* word 0, bits 12-8 = 0a */
 67    "ACS-IT8 device",                               /* word 0, bits 12-8 = 0b */
 68    "Array controller",                             /* word 0, bits 12-8 = 0c */
 69    "Enclosure services",                           /* word 0, bits 12-8 = 0d */
 70    "Reduced block command device",                 /* word 0, bits 12-8 = 0e */
 71    "Optical card reader/writer",                   /* word 0, bits 12-8 = 0f */
 72    "",                                             /* word 0, bits 12-8 = 10 */
 73    "",                                             /* word 0, bits 12-8 = 11 */
 74    "",                                             /* word 0, bits 12-8 = 12 */
 75    "",                                             /* word 0, bits 12-8 = 13 */
 76    "",                                             /* word 0, bits 12-8 = 14 */
 77    "",                                             /* word 0, bits 12-8 = 15 */
 78    "",                                             /* word 0, bits 12-8 = 16 */
 79    "",                                             /* word 0, bits 12-8 = 17 */
 80    "",                                             /* word 0, bits 12-8 = 18 */
 81    "",                                             /* word 0, bits 12-8 = 19 */
 82    "",                                             /* word 0, bits 12-8 = 1a */
 83    "",                                             /* word 0, bits 12-8 = 1b */
 84    "",                                             /* word 0, bits 12-8 = 1c */
 85    "",                                             /* word 0, bits 12-8 = 1d */
 86    "",                                             /* word 0, bits 12-8 = 1e */
 87    "Unknown",                                      /* word 0, bits 12-8 = 1f */
 88  };
 89  
 90  /* older kernels (2.2.x) have incomplete id structure for IDE devices */
 91  #ifndef __NEW_HD_DRIVE_ID
 92  #define command_set_1 command_sets
 93  #define command_set_2 word83
 94  #define hw_config word93
 95  #endif
 96  
 97  static unsigned long long get_longlong(const string & path)
 98  {
 99    FILE *in = fopen(path.c_str(), "r");
100    unsigned long long l = 0;
101  
102    if (in)
103    {
104      if(fscanf(in, "%lld", &l) < 1)
105        l = 0;
106      fclose(in);
107    }
108  
109    return l;
110  }
111  
112  
113  static string get_pciid(const string & bus,
114  const string & device)
115  {
116    char buffer[20];
117    int pcibus, pcidevfunc;
118  
119    sscanf(bus.c_str(), "%x", &pcibus);
120    sscanf(device.c_str(), "%x", &pcidevfunc);
121    snprintf(buffer, sizeof(buffer), "%02x:%02x.%x", pcibus,
122      PCI_SLOT(pcidevfunc), PCI_FUNC(pcidevfunc));
123  
124    return string(buffer);
125  }
126  
127  
128  static bool probe_ide(const string & name,
129  hwNode & device)
130  {
131    struct hd_driveid id;
132    const u_int8_t *id_regs = (const u_int8_t *) &id;
133    int fd = open(device.getLogicalName().c_str(), O_RDONLY | O_NONBLOCK);
134  
135    if (fd < 0)
136      return false;
137  
138    memset(&id, 0, sizeof(id));
139    if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0)
140    {
141      close(fd);
142      return false;
143    }
144  
145    u_int8_t args[4 + 512] = { WIN_IDENTIFY, 0, 0, 1, };
146  
147    if(id.config & 0x8000)                          // we have a packet device
148    {
149      args[0] = WIN_PIDENTIFY;                      // so use the right command to avoid kernel messages (Aborted Command)
150      device.addCapability("packet", "ATAPI packet device");
151    }
152  
153    if (ioctl(fd, HDIO_DRIVE_CMD, &args) != 0)
154    {
155      close(fd);
156      return false;
157    }
158  
159    close(fd);
160  
161    u_int16_t pidentity[256];
162    for (int i = 0; i < 256; i++)
163      pidentity[i] = args[4 + 2 * i] + (args[4 + 2 * i + 1] << 8);
164  
165    if (id.model[0])
166      device.setProduct(hw::strip(string((char *) id.model, sizeof(id.model))));
167    if (id.fw_rev[0])
168      device.
169        setVersion(hw::strip(string((char *) id.fw_rev, sizeof(id.fw_rev))));
170    if (id.serial_no[0])
171      device.
172        setSerial(hw::
173        strip(string((char *) id.serial_no, sizeof(id.serial_no))));
174  
175    if (!(pidentity[GEN_CONFIG] & NOT_ATA))
176    {
177      device.addCapability("ata", "ATA");
178      device.setDescription("ATA Disk");
179    }
180    else if (!(pidentity[GEN_CONFIG] & NOT_ATAPI))
181    {
182      u_int8_t eqpt = (pidentity[GEN_CONFIG] & EQPT_TYPE) >> 8;
183      device.addCapability("atapi", "ATAPI");
184  
185      if (eqpt == CD_ROM)
186        device.addCapability("cdrom", "can read CD-ROMs");
187  
188      if (eqpt < 0x20)
189        device.setDescription("IDE " + string(description[eqpt]));
190    }
191    if (id.config & (1 << 7))
192      device.addCapability("removable", "support is removable");
193    if (id.config & (1 << 15))
194      device.addCapability("nonmagnetic", "support is non-magnetic (optical)");
195    if (id.capability & 1)
196      device.addCapability("dma", "Direct Memory Access");
197    if (id.capability & 2)
198      device.addCapability("lba", "Large Block Addressing");
199    if (id.capability & 8)
200      device.addCapability("iordy", "I/O ready reporting");
201    if (id.command_set_1 & 1)
202      device.addCapability("smart",
203        "S.M.A.R.T. (Self-Monitoring And Reporting Technology)");
204    if (id.command_set_1 & 2)
205      device.addCapability("security", "ATA security extensions");
206    if (id.command_set_1 & 4)
207      device.addCapability("removable", "support is removable");
208    if (id.command_set_1 & 8)
209      device.addCapability("pm", "Power Management");
210    if (id.command_set_2 & 8)
211      device.addCapability("apm", "Advanced Power Management");
212  
213    if ((id.capability & 8) || (id.field_valid & 2))
214    {
215      if (id.field_valid & 4)
216      {
217        if (id.dma_ultra & 0x100)
218          device.setConfig("mode", "udma0");
219        if (id.dma_ultra & 0x200)
220          device.setConfig("mode", "udma1");
221        if (id.dma_ultra & 0x400)
222          device.setConfig("mode", "udma2");
223        if (id.hw_config & 0x2000)
224        {
225          if (id.dma_ultra & 0x800)
226            device.setConfig("mode", "udma3");
227          if (id.dma_ultra & 0x1000)
228            device.setConfig("mode", "udma4");
229          if (id.dma_ultra & 0x2000)
230            device.setConfig("mode", "udma5");
231          if (id.dma_ultra & 0x4000)
232            device.setConfig("mode", "udma6");
233          if (id.dma_ultra & 0x8000)
234            device.setConfig("mode", "udma7");
235        }
236      }
237    }
238  
239    if (id_regs[83] & 8)
240      device.addCapability("apm", "Advanced Power Management");
241  
242  //if (device.isCapable("iordy") && (id.capability & 4))
243  //device.setConfig("iordy", "yes");
244  
245    if (device.isCapable("smart"))
246    {
247      if (id.command_set_2 & (1 << 14))
248        device.setConfig("smart", "on");
249      else
250        device.setConfig("smart", "off");
251    }
252  
253    if (device.isCapable("apm"))
254    {
255      if (!(id_regs[86] & 8))
256        device.setConfig("apm", "off");
257      else
258        device.setConfig("apm", "on");
259    }
260  
261    if (device.isCapable("lba"))
262      device.setCapacity((unsigned long long) id.lba_capacity * 512);
263    if (device.isCapable("removable"))
264      device.setCapacity(0);                        // we'll first have to make sure we have a disk
265  
266    //if(pidentity[168] && (pidentity[168] & 0xfff8) == 0) {
267      switch(pidentity[168]) {
268                  case 1:
269                          device.setConfig("size", "5.25 inch");
270                          break;
271                  case 2:
272                          device.setConfig("size", "3.5 inch");
273                          break;
274                  case 3:
275                          device.setConfig("size", "2.5 inch");
276                          break;
277                  case 4:
278                          device.setConfig("size", "1.8 inch");
279                          break;
280                  case 5:
281                          device.setConfig("size", "less than 1.8 inch");
282                          break;
283                  }
284    //}
285  
286    if (device.isCapable("cdrom"))
287      scan_cdrom(device);
288    else
289      scan_disk(device);
290  
291  #if 0
292    if (!(iddata[GEN_CONFIG] & NOT_ATA))
293      device.addCapability("ata");
294    else if (iddata[GEN_CONFIG] == CFA_SUPPORT_VAL)
295    {
296      device.addCapability("ata");
297      device.addCapability("compactflash");
298    }
299    else if (!(iddata[GEN_CONFIG] & NOT_ATAPI))
300    {
301      device.addCapability("atapi");
302      device.setDescription(description[(iddata[GEN_CONFIG] & EQPT_TYPE) >> 8]);
303    }
304  
305    if (iddata[START_MODEL])
306      device.setProduct(print_ascii((char *) &iddata[START_MODEL],
307        LENGTH_MODEL));
308    if (iddata[START_SERIAL])
309      device.
310        setSerial(print_ascii((char *) &iddata[START_SERIAL], LENGTH_SERIAL));
311    if (iddata[START_FW_REV])
312      device.
313        setVersion(print_ascii((char *) &iddata[START_FW_REV], LENGTH_FW_REV));
314    if (iddata[CAPAB_0] & LBA_SUP)
315      device.addCapability("lba");
316    if (iddata[CAPAB_0] & IORDY_SUP)
317      device.addCapability("iordy");
318    if (iddata[CAPAB_0] & IORDY_OFF)
319    {
320      device.addCapability("iordy");
321      device.addCapability("iordyoff");
322    }
323    if (iddata[CAPAB_0] & DMA_SUP)
324      device.addCapability("dma");
325    if (iddata[CAPAB_0] & DMA_IL_SUP)
326    {
327      device.addCapability("interleaved_dma");
328      device.addCapability("dma");
329    }
330    if (iddata[CAPAB_0] & CMD_Q_SUP)
331      device.addCapability("command_queuing");
332    if (iddata[CAPAB_0] & OVLP_SUP)
333      device.addCapability("overlap_operation");
334  #endif
335    return true;
336  }
337  
338  
339  static bool is_master(const string & device)
340  {
341    if (device == "")
342      return false;
343  
344    switch ((device[device.length() - 1] - 'a') % 2)
345    {
346      case 0:
347        return true;
348      case 1:
349        return false;
350      default:
351        return false;
352    }
353  }
354  
355  bool scan_ide(hwNode & n)
356  {
357    struct dirent **namelist;
358    int nentries;
359  
360    pushd(PROC_IDE);
361    nentries = scandir(".", &namelist, selectdir, alphasort);
362    popd();
363  
364    if (nentries < 0)
365      return false;
366  
367    for (int i = 0; i < nentries; i++)
368    {
369      vector < string > config;
370      hwNode ide("ide",
371        hw::bus);
372  
373      ide.setLogicalName(namelist[i]->d_name);
374      ide.setHandle("IDE:" + string(namelist[i]->d_name));
375  
376      if (exists(string(PROC_IDE"/") + namelist[i]->d_name + "/channel"))
377      {
378        vector < string > identify;
379        string channel = "";
380        char *id = namelist[i]->d_name;
381  
382        while ((*id != 0) && (!isdigit(*id)))
383          id++;
384        if (*id != 0)
385        {
386          ide.setBusInfo("ide@" + string(id));
387          ide.setPhysId(string(id));
388        }
389  
390        loadfile(string(PROC_IDE"/") + namelist[i]->d_name + "/config", config);
391        if (config.size() > 0)
392          splitlines(config[0], identify, ' ');
393        config.clear();
394        loadfile(string(PROC_IDE"/") + namelist[i]->d_name + "/channel", config);
395        if (config.size() > 0)
396          channel = config[0];
397        config.clear();
398  
399  //if (identify.size() >= 1)
400        {
401          struct dirent **devicelist;
402          int ndevices;
403  
404          pushd(string(PROC_IDE) + "/" + namelist[i]->d_name);
405          ndevices = scandir(".", &devicelist, selectdir, alphasort);
406          popd();
407  
408          for (int j = 0; j < ndevices; j++)
409          {
410            string basepath =
411              string(PROC_IDE) + "/" + namelist[i]->d_name + "/" +
412              devicelist[j]->d_name;
413            hwNode idedevice("device",
414              hw::storage);
415  
416            idedevice =
417              hwNode(get_string(basepath + "/media", "disk"), hw::disk);
418  
419            idedevice.setCapacity(512 * get_longlong(basepath + "/capacity"));
420            idedevice.setLogicalName(string("/dev/") + devicelist[j]->d_name);
421            idedevice.setProduct(get_string(basepath + "/model"));
422            idedevice.claim();
423            idedevice.setHandle(ide.getHandle() + ":" +
424              string(devicelist[j]->d_name));
425            if (is_master(devicelist[j]->d_name))
426              idedevice.setPhysId(0);
427            else
428              idedevice.setPhysId(1);
429            idedevice.setBusInfo(ide.getBusInfo() + "." +
430              idedevice.getPhysId());
431  
432            probe_ide(devicelist[j]->d_name, idedevice);
433  
434            ide.addChild(idedevice);
435            free(devicelist[j]);
436          }
437          free(devicelist);
438  
439          ide.setDescription(hw::strip("IDE Channel " + hw::strip(channel)));
440  
441          if ( identify.size() == 11 && identify[0] == "pci")
442          {
443            string businfo = guessBusInfo(get_pciid(identify[2], identify[4]));
444            hwNode *parent = n.findChildByBusInfo(businfo);
445  
446            if (parent)
447            {
448              parent->claim();
449              ide.setClock(parent->getClock());
450              parent->addChild(ide);
451            }
452          }
453          else                                      // we have to guess the parent device
454          {
455            hwNode * parent = guessParent(ide, n);
456            if(parent)
457            {
458              parent->claim();
459              ide.setClock(parent->getClock());
460              parent->addChild(ide);
461            }
462            else
463              for (unsigned int k = 0; k < ide.countChildren(); k++)
464            {
465              hwNode *candidate =
466                n.findChildByLogicalName(ide.getChild(k)->getLogicalName());
467  
468              if (candidate)
469              {
470                parent = candidate;
471                candidate->merge(*ide.getChild(k));
472                break;
473              }
474            }
475            if(!parent) n.addChild(ide);
476          }
477        }
478  
479      }
480  
481      free(namelist[i]);
482    }
483    free(namelist);
484  
485    return false;
486  }