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 }