usb.cc
1 /* 2 * usb.cc 3 * 4 */ 5 6 #ifndef _GNU_SOURCE 7 #define _GNU_SOURCE 8 #endif 9 10 #include "version.h" 11 #include "config.h" 12 #include "usb.h" 13 #include "osutils.h" 14 #include "heuristics.h" 15 #include "options.h" 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <map> 19 #include <sys/types.h> 20 #include <sys/stat.h> 21 #include <sys/ioctl.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <string.h> 25 #include <stdlib.h> 26 #include <unistd.h> 27 #include <dirent.h> 28 #include <cstring> 29 30 #define PROCBUSUSBDEVICES "/proc/bus/usb/devices" 31 #define SYSKERNELDEBUGUSBDEVICES "/sys/kernel/debug/usb/devices" 32 #define USBID_PATH DATADIR"/usb.ids:/usr/share/lshw/usb.ids:/usr/local/share/usb.ids:/usr/share/usb.ids:/etc/usb.ids:/usr/share/hwdata/usb.ids:/usr/share/misc/usb.ids" 33 34 #define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ 35 #define USB_CLASS_AUDIO 1 36 #define USB_CLASS_COMM 2 37 #define USB_CLASS_HID 3 38 #define USB_CLASS_IMAGING 6 39 #define USB_CLASS_PRINTER 7 40 #define USB_CLASS_MASS_STORAGE 8 41 #define USB_CLASS_HUB 9 42 #define USB_CLASS_DATA 0xa 43 #define USB_CLASS_SMARTCARD 0xb 44 #define USB_CLASS_VIDEO 0xe 45 #define USB_CLASS_WIRELESS 0xe0 46 #define USB_CLASS_VENDOR_SPEC 0xff 47 48 #define USB_SC_AUDIOCONTROL 1 49 #define USB_SC_AUDIOSTREAMING 2 50 #define USB_SC_AUDIOMIDISTREAMING 3 51 #define USB_SC_COMMMODEM 2 52 #define USB_SC_COMMETHERNET 6 53 #define USB_SC_COMMOBEX 0xb 54 #define USB_SC_HIDNONE 0 55 #define USB_SC_HIDBOOT 1 56 #define USB_PROT_HIDKBD 1 57 #define USB_PROT_HIDMOUSE 2 58 #define USB_SC_PRINTER 1 59 #define USB_PROT_PRINTERUNIDIR 1 60 #define USB_PROT_PRINTERBIDIR 2 61 #define USB_PROT_PRINTER1284 3 62 #define USB_SC_STORAGERBC 1 63 #define USB_SC_STORAGEATAPI 2 64 #define USB_SC_STORAGEFLOPPY 4 65 #define USB_SC_STORAGESCSI 6 66 #define USB_SC_WIRELESSRADIO 1 67 #define USB_PROT_BLUETOOTH 1 68 69 static map<u_int16_t,string> usbvendors; 70 static map<u_int32_t,string> usbproducts; 71 72 #define PRODID(x, y) ((x << 16) + y) 73 74 __ID("@(#) $Id$"); 75 76 static string usbhost(unsigned bus) 77 { 78 char buffer[10]; 79 80 snprintf(buffer, sizeof(buffer), "usb%u", bus); 81 82 return string(buffer); 83 } 84 85 86 static string usbhandle(unsigned bus, unsigned level, unsigned dev) 87 { 88 char buffer[10]; 89 90 snprintf(buffer, sizeof(buffer), "USB:%u:%u", bus, dev); 91 92 return string(buffer); 93 } 94 95 96 static string usbbusinfo(unsigned bus, unsigned level, unsigned port) 97 { 98 char buffer[10]; 99 100 if(level>0) 101 snprintf(buffer, sizeof(buffer), "usb@%u:%u", bus, port); 102 else 103 snprintf(buffer, sizeof(buffer), "usb@%u", bus); 104 105 return string(buffer); 106 } 107 108 109 static string usbspeed(float speed) 110 { 111 char buffer[15]; 112 113 snprintf(buffer, sizeof(buffer), "%.fMbit/s", speed); 114 115 return string(buffer); 116 } 117 118 119 static bool addUSBChild(hwNode & n, hwNode & device, unsigned bus, unsigned lev, unsigned prnt) 120 { 121 hwNode * parent = NULL; 122 123 device.addHint("bus.icon", string("usb")); 124 125 if(prnt>0) parent = n.findChildByHandle(usbhandle(bus, lev-1, prnt)); 126 if(parent) 127 { 128 if(parent->getBusInfo().find(":")==string::npos) 129 device.setBusInfo(parent->getBusInfo()+":"+device.getPhysId()); 130 else 131 device.setBusInfo(parent->getBusInfo()+"."+device.getPhysId()); 132 parent->addChild(device); 133 return true; 134 } 135 else 136 { 137 // USB host 138 { 139 string businfo = guessBusInfo(device.getSerial()); 140 parent = n.findChildByBusInfo(businfo); 141 if(!parent) // still no luck 142 { 143 unsigned long long ioport = strtoll(device.getSerial().c_str(), NULL, 16); 144 parent = n.findChildByResource(hw::resource::ioport(ioport, ioport)); 145 } 146 device.setSerial(""); // serial# has no meaning for USB hosts 147 } 148 if(parent) 149 { 150 parent->addChild(device); 151 return true; 152 } 153 else 154 n.addChild(device); 155 return false; 156 } 157 } 158 159 160 static bool setUSBClass(hwNode & device, unsigned cls, unsigned sub, unsigned prot) 161 { 162 if(device.getClass()!=hw::generic) return false; 163 switch(cls) 164 { 165 case USB_CLASS_AUDIO: 166 device.setClass(hw::multimedia); 167 device.setDescription(_("Audio device")); 168 switch(sub) 169 { 170 case USB_SC_AUDIOCONTROL: 171 device.addCapability("audio-control", _("Control device")); 172 break; 173 case USB_SC_AUDIOMIDISTREAMING: 174 device.addCapability("midi", _("MIDI")); 175 case USB_SC_AUDIOSTREAMING: 176 device.addCapability("audio-streaming", _("Audio streaming")); 177 break; 178 } 179 break; 180 case USB_CLASS_COMM: 181 device.setClass(hw::communication); 182 device.setDescription(_("Communication device")); 183 if(sub == USB_SC_COMMMODEM) 184 { 185 device.setDescription(_("Modem")); 186 if((prot>=1) && (prot<=6)) device.addCapability("atcommands", _("AT (Hayes) compatible")); 187 } 188 if(sub==USB_SC_COMMETHERNET) device.addCapability("ethernet", _("Ethernet networking")); 189 if(sub==USB_SC_COMMOBEX) device.addCapability("obex", _("OBEX networking")); 190 break; 191 case USB_CLASS_HID: 192 device.setClass(hw::input); 193 device.setDescription(_("Human interface device")); 194 if((sub==USB_SC_HIDNONE)||(sub==USB_SC_HIDBOOT)) 195 { 196 switch(prot) 197 { 198 case USB_PROT_HIDKBD: 199 device.setDescription(_("Keyboard")); 200 break; 201 case USB_PROT_HIDMOUSE: 202 device.setDescription(_("Mouse")); 203 break; 204 } 205 } 206 break; 207 case USB_CLASS_PRINTER: 208 device.setClass(hw::printer); 209 device.setDescription(_("Printer")); 210 device.addHint("icon", string("printer")); 211 if(sub==USB_SC_PRINTER) 212 { 213 switch(prot) 214 { 215 case USB_PROT_PRINTERUNIDIR: 216 device.addCapability("unidirectional", _("Unidirectional")); 217 break; 218 case USB_PROT_PRINTERBIDIR: 219 device.addCapability("bidirectional", _("Bidirectional")); 220 break; 221 case USB_PROT_PRINTER1284: 222 device.addCapability("ieee1284.4", _("IEEE 1284.4 compatible bidirectional")); 223 break; 224 } 225 } 226 break; 227 case USB_CLASS_MASS_STORAGE: 228 device.setClass(hw::storage); 229 device.setDescription(_("Mass storage device")); 230 switch(sub) 231 { 232 case USB_SC_STORAGERBC: 233 device.addCapability("flash", _("RBC (typically Flash) mass storage")); 234 break; 235 case USB_SC_STORAGEATAPI: 236 device.addCapability("atapi", _("SFF-8020i, MMC-2 (ATAPI)")); 237 break; 238 case USB_SC_STORAGEFLOPPY: 239 device.addCapability("floppy", _("Floppy (UFI)")); 240 break; 241 case USB_SC_STORAGESCSI: 242 device.addCapability("scsi", _("SCSI")); 243 break; 244 } 245 break; 246 case USB_CLASS_HUB: 247 device.setClass(hw::bus); 248 device.setDescription(_("USB hub")); 249 break; 250 case USB_CLASS_DATA: 251 device.setClass(hw::generic); 252 break; 253 case USB_CLASS_SMARTCARD: 254 device.setClass(hw::generic); 255 device.setDescription(_("Smart card reader")); 256 break; 257 case USB_CLASS_VIDEO: 258 device.setClass(hw::multimedia); 259 device.setDescription(_("Video")); 260 break; 261 case USB_CLASS_WIRELESS: 262 device.setClass(hw::communication); 263 device.setDescription(_("Wireless interface")); 264 if((sub==USB_SC_WIRELESSRADIO) && (prot==USB_PROT_BLUETOOTH)) 265 { 266 device.setDescription(_("Bluetooth wireless interface")); 267 device.addCapability("bluetooth", _("Bluetooth wireless radio")); 268 device.addHint("icon", string("bluetooth")); 269 } 270 break; 271 default: 272 device.setDescription(_("Generic USB device")); 273 return false; 274 } 275 276 return true; 277 } 278 279 280 static bool describeUSB(hwNode & device, unsigned vendor, unsigned prodid) 281 { 282 if(usbvendors.find(vendor)==usbvendors.end()) return false; 283 284 device.setVendor(usbvendors[vendor]+(enabled("output:numeric")?" ["+tohex(vendor)+"]":"")); 285 device.addHint("usb.idVendor", vendor); 286 device.addHint("usb.idProduct", prodid); 287 288 if(usbproducts.find(PRODID(vendor, prodid))!=usbproducts.end()) 289 device.setProduct(usbproducts[PRODID(vendor, prodid)]+(enabled("output:numeric")?" ["+tohex(vendor)+":"+tohex(prodid)+"]":"")); 290 291 return true; 292 } 293 294 295 static bool load_usbids(const string & name) 296 { 297 FILE * usbids = NULL; 298 u_int16_t vendorid = 0; 299 300 usbids = fopen(name.c_str(), "r"); 301 if(!usbids) return false; 302 303 while(!feof(usbids)) 304 { 305 char * buffer = NULL; 306 size_t linelen; 307 unsigned t = 0; 308 char *description = NULL; 309 310 if(getline(&buffer, &linelen, usbids)>0) 311 { 312 if(buffer[linelen-1]<' ') 313 buffer[linelen-1] = '\0'; // chop \n 314 string line = string(buffer); 315 free(buffer); 316 buffer = NULL; 317 318 description = NULL; 319 t = 0; 320 if(line.length()>0) 321 { 322 if(line[0] == '\t') // product id entry 323 { 324 line.erase(0, 1); 325 if(matches(line, "^[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]")) 326 t = strtol(line.c_str(), &description, 16); 327 if(description && (description != line.c_str())) 328 { 329 usbproducts[PRODID(vendorid,t)] = hw::strip(description); 330 } 331 } 332 else // vendor id entry 333 { 334 if(matches(line, "^[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]")) 335 t = strtol(line.c_str(), &description, 16); 336 if(description && (description != line.c_str())) 337 { 338 vendorid = t; 339 usbvendors[t] = hw::strip(description); 340 } 341 } 342 } 343 } 344 if(buffer != NULL) free(buffer); 345 } 346 347 fclose(usbids); 348 349 return true; 350 } 351 352 353 bool scan_usb(hwNode & n) 354 { 355 hwNode device("device"); 356 FILE * usbdevices = NULL; 357 bool defined = false; 358 unsigned int bus,lev,prnt,port,cnt,devnum,mxch; 359 float speed; 360 char ver[10+1]; 361 unsigned int cls, sub, prot, mxps, numcfgs; 362 unsigned int vendor, prodid; 363 char rev[10+1]; 364 unsigned numifs, cfgnum, atr; 365 char mxpwr[10+1]; 366 unsigned ifnum, alt, numeps; 367 char driver[80+1]; 368 369 if (!exists(SYSKERNELDEBUGUSBDEVICES) && !exists(PROCBUSUSBDEVICES)) 370 return false; 371 372 vector < string > filenames; 373 splitlines(USBID_PATH, filenames, ':'); 374 for (int i = filenames.size() - 1; i >= 0; i--) 375 { 376 load_usbids(filenames[i]); 377 } 378 filenames.clear(); 379 380 if (exists(SYSKERNELDEBUGUSBDEVICES)) 381 usbdevices = fopen(SYSKERNELDEBUGUSBDEVICES, "r"); 382 383 if(!usbdevices && exists(PROCBUSUSBDEVICES)) 384 usbdevices = fopen(PROCBUSUSBDEVICES, "r"); 385 386 if(!usbdevices) 387 return false; 388 389 while(!feof(usbdevices)) 390 { 391 char * buffer = NULL; 392 size_t linelen; 393 char strname[80+1]; 394 char strval[80+1]; 395 396 if(getline(&buffer, &linelen, usbdevices)>0) 397 { 398 string line = hw::strip(string(buffer)); 399 free(buffer); 400 buffer = NULL; 401 402 if(line.length()<=0) 403 { 404 if(defined) 405 addUSBChild(n, device, bus, lev, prnt); 406 device = hwNode("device"); 407 defined = false; 408 } 409 else 410 { 411 if((line.length()>1) && (line[1] == ':')) 412 switch(line[0]) 413 { 414 case 'T': 415 bus = lev = prnt = port = cnt = devnum = mxch = 0; 416 speed = 0.0; 417 strcpy(ver, ""); 418 strcpy(rev, ""); 419 cls = sub = prot = mxps = numcfgs = 0; 420 vendor = prodid = 0; 421 if(sscanf(line.c_str(), "T: Bus=%u Lev=%u Prnt=%u Port=%u Cnt=%u Dev#=%u Spd=%f MxCh=%u", &bus, &lev, &prnt, &port, &cnt, &devnum, &speed, &mxch)>0) 422 { 423 defined = true; 424 if(lev==0) 425 { 426 device = hwNode("usbhost", hw::bus); 427 device.claim(); 428 device.setLogicalName(usbhost(bus)); 429 } 430 else 431 device = hwNode("usb"); 432 device.setHandle(usbhandle(bus, lev, devnum)); 433 device.setBusInfo(usbbusinfo(bus, lev, port)); 434 device.setPhysId(port+1); 435 device.setConfig("speed", usbspeed(speed)); 436 if(mxch>0) 437 { 438 snprintf(strval, sizeof(strval), "%u", mxch); 439 device.setConfig("slots", strval); 440 } 441 } 442 break; 443 case 'D': 444 strcpy(ver, ""); 445 cls = sub = prot = mxps = numcfgs = 0; 446 if(sscanf(line.c_str(), "D: Ver=%s Cls=%x(%*5c) Sub=%x Prot=%x MxPS=%u #Cfgs=%u", ver, &cls, &sub, &prot, &mxps, &numcfgs)>0) 447 { 448 setUSBClass(device, cls, sub, prot); 449 device.addCapability(string("usb-")+string(ver)); 450 device.describeCapability("usb-1.00", "USB 1.0"); 451 device.describeCapability("usb-1.10", "USB 1.1"); 452 device.describeCapability("usb-2.00", "USB 2.0"); 453 device.addHint("usb.bDeviceClass", cls); 454 device.addHint("usb.bDeviceSubClass", sub); 455 device.addHint("usb.bDeviceProtocol", prot); 456 } 457 break; 458 case 'P': 459 vendor = prodid = 0; 460 strcpy(rev, ""); 461 if(sscanf(line.c_str(), "P: Vendor=%x ProdID=%x Rev=%10s", &vendor, &prodid, rev)>0) 462 { 463 describeUSB(device, vendor, prodid); 464 device.setVersion(hw::strip(rev)); 465 } 466 break; 467 case 'S': 468 memset(strname, 0, sizeof(strname)); 469 memset(strval, 0, sizeof(strval)); 470 if(sscanf(line.c_str(), "S: %80[^=]=%80[ -z]", strname, strval)>0) 471 { 472 if(strcasecmp(strname, "Manufacturer")==0) 473 device.setVendor(hw::strip(strval)+(enabled("output:numeric")?" ["+tohex(vendor)+"]":"")); 474 if(strcasecmp(strname, "Product")==0) 475 device.setProduct(hw::strip(strval)+(enabled("output:numeric")?" ["+tohex(vendor)+":"+tohex(prodid)+"]":"")); 476 if(strcasecmp(strname, "SerialNumber")==0) 477 device.setSerial(hw::strip(strval)); 478 } 479 break; 480 case 'C': 481 numifs = cfgnum = atr = 0; 482 strcpy(mxpwr, ""); 483 if(sscanf(line.c_str(), "C:* #Ifs=%u Cfg#=%u Atr=%x MxPwr=%s", &numifs, &cfgnum, &atr, mxpwr)>0) 484 { 485 if(strcmp("0mA", mxpwr)!=0) 486 device.setConfig("maxpower", mxpwr); 487 } 488 break; 489 case 'I': 490 ifnum = alt = numeps = cls = sub = prot = 0; 491 memset(driver, 0, sizeof(driver)); 492 if(((sscanf(line.c_str(), "I:* If#=%u Alt=%u #EPs=%u Cls=%x(%*5c) Sub=%x Prot=%x Driver=%80[ -z]", &ifnum, &alt, &numeps, &cls, &sub, &prot, driver)>0) && (cfgnum>0)) || ((sscanf(line.c_str(), "I: If#=%u Alt=%u #EPs=%u Cls=%x(%*5c) Sub=%x Prot=%x Driver=%80[ -z]", &ifnum, &alt, &numeps, &cls, &sub, &prot, driver)>0) && (cfgnum>0))) 493 { 494 setUSBClass(device, cls, sub, prot); 495 if((strlen(driver)!=0) && (strcasecmp("(none)", driver)!=0)) 496 { 497 device.setConfig("driver", hw::strip(driver)); 498 device.claim(); 499 } 500 } 501 break; 502 } 503 } 504 } 505 if(buffer != NULL) free(buffer); 506 } 507 if(defined) 508 addUSBChild(n, device, bus, lev, prnt); 509 510 if(usbdevices) fclose(usbdevices); 511 512 return true; 513 }