Back to index

lshw  02.16
usb.cc
Go to the documentation of this file.
00001 /*
00002  * usb.cc
00003  *
00004  */
00005 
00006 #ifndef _GNU_SOURCE
00007 #define _GNU_SOURCE
00008 #endif
00009 
00010 #include "version.h"
00011 #include "config.h"
00012 #include "usb.h"
00013 #include "osutils.h"
00014 #include "heuristics.h"
00015 #include "options.h"
00016 #include <stdio.h>
00017 #include <map>
00018 #include <sys/types.h>
00019 #include <sys/stat.h>
00020 #include <sys/ioctl.h>
00021 #include <errno.h>
00022 #include <fcntl.h>
00023 #include <string.h>
00024 #include <stdlib.h>
00025 #include <unistd.h>
00026 #include <dirent.h>
00027 
00028 #define PROCBUSUSBDEVICES "/proc/bus/usb/devices"
00029 #define SYSBUSUSBDEVICES "/sys/bus/usb/devices"
00030 #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"
00031 
00032 #define USB_CLASS_PER_INTERFACE         0         /* for DeviceClass */
00033 #define USB_CLASS_AUDIO                 1
00034 #define USB_CLASS_COMM                  2
00035 #define USB_CLASS_HID                   3
00036 #define USB_CLASS_IMAGING               6
00037 #define USB_CLASS_PRINTER               7
00038 #define USB_CLASS_MASS_STORAGE          8
00039 #define USB_CLASS_HUB                   9
00040 #define USB_CLASS_DATA                  0xa
00041 #define USB_CLASS_SMARTCARD             0xb
00042 #define USB_CLASS_VIDEO                 0xe
00043 #define USB_CLASS_WIRELESS              0xe0
00044 #define USB_CLASS_VENDOR_SPEC           0xff
00045 
00046 #define USB_SC_AUDIOCONTROL   1
00047 #define USB_SC_AUDIOSTREAMING   2
00048 #define USB_SC_AUDIOMIDISTREAMING 3
00049 #define USB_SC_COMMMODEM    2
00050 #define USB_SC_COMMETHERNET   6
00051 #define USB_SC_COMMOBEX     0xb
00052 #define USB_SC_HIDNONE      0
00053 #define USB_SC_HIDBOOT      1
00054 #define USB_PROT_HIDKBD     1
00055 #define USB_PROT_HIDMOUSE   2
00056 #define USB_SC_PRINTER      1
00057 #define USB_PROT_PRINTERUNIDIR    1
00058 #define USB_PROT_PRINTERBIDIR   2
00059 #define USB_PROT_PRINTER1284    3
00060 #define USB_SC_STORAGERBC   1
00061 #define USB_SC_STORAGEATAPI   2
00062 #define USB_SC_STORAGEFLOPPY    4
00063 #define USB_SC_STORAGESCSI    6
00064 #define USB_SC_WIRELESSRADIO    1
00065 #define USB_PROT_BLUETOOTH    1
00066 
00067 static map<u_int16_t,string> usbvendors;
00068 static map<u_int32_t,string> usbproducts;
00069 
00070 #define PRODID(x, y) ((x << 16) + y)
00071 
00072 __ID("@(#) $Id: usb.cc 2433 2012-01-10 22:01:30Z lyonel $");
00073 
00074 static string usbhost(unsigned bus)
00075 {
00076   char buffer[10];
00077 
00078   snprintf(buffer, sizeof(buffer), "usb%u", bus);
00079 
00080   return string(buffer);
00081 }
00082 
00083 
00084 static string usbhandle(unsigned bus, unsigned level, unsigned dev)
00085 {
00086   char buffer[10];
00087 
00088   snprintf(buffer, sizeof(buffer), "USB:%u:%u", bus, dev);
00089 
00090   return string(buffer);
00091 }
00092 
00093 
00094 static string usbbusinfo(unsigned bus, unsigned level, unsigned port)
00095 {
00096   char buffer[10];
00097 
00098   if(level>0)
00099     snprintf(buffer, sizeof(buffer), "usb@%u:%u", bus, port);
00100   else
00101     snprintf(buffer, sizeof(buffer), "usb@%u", bus);
00102 
00103   return string(buffer);
00104 }
00105 
00106 
00107 static string usbspeed(float speed)
00108 {
00109   char buffer[15];
00110 
00111   snprintf(buffer, sizeof(buffer), "%.fMbit/s", speed);
00112 
00113   return string(buffer);
00114 }
00115 
00116 
00117 static bool addUSBChild(hwNode & n, hwNode & device, unsigned bus, unsigned lev, unsigned prnt)
00118 {
00119   hwNode * parent = NULL;
00120 
00121   device.addHint("bus.icon", string("usb"));
00122 
00123   if(prnt>0) parent = n.findChildByHandle(usbhandle(bus, lev-1, prnt));
00124   if(parent)
00125   {
00126     if(parent->getBusInfo().find(":")==string::npos)
00127       device.setBusInfo(parent->getBusInfo()+":"+device.getPhysId());
00128     else
00129       device.setBusInfo(parent->getBusInfo()+"."+device.getPhysId());
00130     parent->addChild(device);
00131     return true;
00132   }
00133   else
00134   {
00135 // USB host
00136     {
00137       string businfo = guessBusInfo(device.getSerial());
00138       parent = n.findChildByBusInfo(businfo);
00139       if(!parent)                                 // still no luck
00140       {
00141         unsigned long long ioport = strtoll(device.getSerial().c_str(), NULL, 16);
00142         parent = n.findChildByResource(hw::resource::ioport(ioport, ioport));
00143       }
00144       device.setSerial("");                       // serial# has no meaning for USB hosts
00145     }
00146     if(parent)
00147     {
00148       parent->addChild(device);
00149       return true;
00150     }
00151     else
00152       n.addChild(device);
00153     return false;
00154   }
00155 }
00156 
00157 
00158 static bool setUSBClass(hwNode & device, unsigned cls, unsigned sub, unsigned prot)
00159 {
00160   if(device.getClass()!=hw::generic) return false;
00161   switch(cls)
00162   {
00163     case USB_CLASS_AUDIO:
00164       device.setClass(hw::multimedia);
00165       device.setDescription(_("Audio device"));
00166       switch(sub)
00167       {
00168         case USB_SC_AUDIOCONTROL:
00169           device.addCapability("audio-control", _("Control device"));
00170           break;
00171         case USB_SC_AUDIOMIDISTREAMING:
00172           device.addCapability("midi", _("MIDI"));
00173         case USB_SC_AUDIOSTREAMING:
00174           device.addCapability("audio-streaming", _("Audio streaming"));
00175           break;
00176       }
00177       break;
00178     case USB_CLASS_COMM:
00179       device.setClass(hw::communication);
00180       device.setDescription(_("Communication device"));
00181       if(sub == USB_SC_COMMMODEM)
00182       {
00183         device.setDescription(_("Modem"));
00184         if((prot>=1) && (prot<=6)) device.addCapability("atcommands", _("AT (Hayes) compatible"));
00185       }
00186       if(sub==USB_SC_COMMETHERNET) device.addCapability("ethernet", _("Ethernet networking"));
00187       if(sub==USB_SC_COMMOBEX) device.addCapability("obex", _("OBEX networking"));
00188       break;
00189     case USB_CLASS_HID:
00190       device.setClass(hw::input);
00191       device.setDescription(_("Human interface device"));
00192       if((sub==USB_SC_HIDNONE)||(sub==USB_SC_HIDBOOT))
00193       {
00194         switch(prot)
00195         {
00196           case USB_PROT_HIDKBD:
00197             device.setDescription(_("Keyboard"));
00198             break;
00199           case USB_PROT_HIDMOUSE:
00200             device.setDescription(_("Mouse"));
00201             break;
00202         }
00203       }
00204       break;
00205     case USB_CLASS_PRINTER:
00206       device.setClass(hw::printer);
00207       device.setDescription(_("Printer"));
00208       device.addHint("icon", string("printer"));
00209       if(sub==USB_SC_PRINTER)
00210       {
00211         switch(prot)
00212         {
00213           case USB_PROT_PRINTERUNIDIR:
00214             device.addCapability("unidirectional", _("Unidirectional"));
00215             break;
00216           case USB_PROT_PRINTERBIDIR:
00217             device.addCapability("bidirectional", _("Bidirectional"));
00218             break;
00219           case USB_PROT_PRINTER1284:
00220             device.addCapability("ieee1284.4", _("IEEE 1284.4 compatible bidirectional"));
00221             break;
00222         }
00223       }
00224       break;
00225     case USB_CLASS_MASS_STORAGE:
00226       device.setClass(hw::storage);
00227       device.setDescription(_("Mass storage device"));
00228       switch(sub)
00229       {
00230         case USB_SC_STORAGERBC:
00231           device.addCapability("flash", _("RBC (typically Flash) mass storage"));
00232           break;
00233         case USB_SC_STORAGEATAPI:
00234           device.addCapability("atapi", _("SFF-8020i, MMC-2 (ATAPI)"));
00235           break;
00236         case USB_SC_STORAGEFLOPPY:
00237           device.addCapability("floppy", _("Floppy (UFI)"));
00238           break;
00239         case USB_SC_STORAGESCSI:
00240           device.addCapability("scsi", _("SCSI"));
00241           break;
00242       }
00243       break;
00244     case USB_CLASS_HUB:
00245       device.setClass(hw::bus);
00246       device.setDescription(_("USB hub"));
00247       break;
00248     case USB_CLASS_DATA:
00249       device.setClass(hw::generic);
00250       break;
00251     case USB_CLASS_SMARTCARD:
00252       device.setClass(hw::generic);
00253       device.setDescription(_("Smart card reader"));
00254       break;
00255     case USB_CLASS_VIDEO:
00256       device.setClass(hw::multimedia);
00257       device.setDescription(_("Video"));
00258       break;
00259     case USB_CLASS_WIRELESS:
00260       device.setClass(hw::communication);
00261       device.setDescription(_("Wireless interface"));
00262       if((sub==USB_SC_WIRELESSRADIO) && (prot==USB_PROT_BLUETOOTH))
00263       {
00264         device.setDescription(_("Bluetooth wireless interface"));
00265         device.addCapability("bluetooth", _("Bluetooth wireless radio"));
00266         device.addHint("icon", string("bluetooth"));
00267       }
00268       break;
00269     default:
00270       device.setDescription(_("Generic USB device"));
00271       return false;
00272   }
00273 
00274   return true;
00275 }
00276 
00277 
00278 static bool describeUSB(hwNode & device, unsigned vendor, unsigned prodid)
00279 {
00280   if(usbvendors.find(vendor)==usbvendors.end()) return false;
00281 
00282   device.setVendor(usbvendors[vendor]+(enabled("output:numeric")?" ["+tohex(vendor)+"]":""));
00283   device.addHint("usb.idVendor", vendor);
00284   device.addHint("usb.idProduct", prodid);
00285 
00286   if(usbproducts.find(PRODID(vendor, prodid))!=usbproducts.end())
00287     device.setProduct(usbproducts[PRODID(vendor, prodid)]+(enabled("output:numeric")?" ["+tohex(vendor)+":"+tohex(prodid)+"]":""));
00288 
00289   return true;
00290 }
00291 
00292 
00293 static bool load_usbids(const string & name)
00294 {
00295   FILE * usbids = NULL;
00296   u_int16_t vendorid = 0;
00297 
00298   usbids = fopen(name.c_str(), "r");
00299   if(!usbids) return false;
00300 
00301   while(!feof(usbids))
00302   {
00303     char * buffer = NULL;
00304     size_t linelen;
00305     unsigned t = 0;
00306     char *description = NULL;
00307 
00308     if(getline(&buffer, &linelen, usbids)>0)
00309     {
00310       if(buffer[linelen-1]<' ')
00311         buffer[linelen-1] = '\0';                 // chop \n
00312       string line = string(buffer);
00313       free(buffer);
00314 
00315       description = NULL;
00316       t = 0;
00317       if(line.length()>0)
00318       {
00319         if(line[0] == '\t')                       // product id entry
00320         {
00321           line.erase(0, 1);
00322           if(matches(line, "^[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]"))
00323             t = strtol(line.c_str(), &description, 16);
00324           if(description && (description != line.c_str()))
00325           {
00326             usbproducts[PRODID(vendorid,t)] = hw::strip(description);
00327           }
00328         }
00329         else                                      // vendor id entry
00330         {
00331           if(matches(line, "^[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]"))
00332             t = strtol(line.c_str(), &description, 16);
00333           if(description && (description != line.c_str()))
00334           {
00335             vendorid = t;
00336             usbvendors[t] = hw::strip(description);
00337           }
00338         }
00339       }
00340     }
00341   }
00342 
00343   fclose(usbids);
00344 
00345   return true;
00346 }
00347 
00348 
00349 bool scan_usb(hwNode & n)
00350 {
00351   hwNode device("device");
00352   FILE * usbdevices = NULL;
00353   bool defined = false;
00354   unsigned int bus,lev,prnt,port,cnt,devnum,mxch;
00355   float speed;
00356   char ver[10+1];
00357   unsigned int cls, sub, prot, mxps, numcfgs;
00358   unsigned int vendor, prodid;
00359   char rev[10+1];
00360   unsigned numifs, cfgnum, atr;
00361   char mxpwr[10+1];
00362   unsigned ifnum, alt, numeps;
00363   char driver[80+1];
00364 
00365   if (!exists(PROCBUSUSBDEVICES))
00366     return false;
00367 
00368   vector < string > filenames;
00369   splitlines(USBID_PATH, filenames, ':');
00370   for (int i = filenames.size() - 1; i >= 0; i--)
00371   {
00372     load_usbids(filenames[i]);
00373   }
00374   filenames.clear();
00375 
00376   usbdevices = fopen(PROCBUSUSBDEVICES, "r");
00377 #if 0
00378   if(!usbdevices)
00379     usbdevices = fopen(SYSBUSUSBDEVICES, "r");
00380 #endif
00381 
00382   while(!feof(usbdevices))
00383   {
00384     char * buffer = NULL;
00385     size_t linelen;
00386     char strname[80+1];
00387     char strval[80+1];
00388 
00389     if(getline(&buffer, &linelen, usbdevices)>0)
00390     {
00391       string line = hw::strip(string(buffer));
00392       free(buffer);
00393 
00394       if(line.length()<=0)
00395       {
00396         if(defined)
00397           addUSBChild(n, device, bus, lev, prnt);
00398         device = hwNode("device");
00399         defined = false;
00400       }
00401       else
00402       {
00403         if((line.length()>1) && (line[1] == ':'))
00404           switch(line[0])
00405           {
00406             case 'T':
00407               bus = lev = prnt = port = cnt = devnum = mxch = 0;
00408               speed = 0.0;
00409               strcpy(ver, "");
00410               strcpy(rev, "");
00411               cls = sub = prot = mxps = numcfgs = 0;
00412               vendor = prodid = 0;
00413               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)
00414               {
00415               defined = true;
00416               if(lev==0)
00417               {
00418                 device = hwNode("usbhost", hw::bus);
00419                 device.claim();
00420                 device.setLogicalName(usbhost(bus));
00421               }
00422               else
00423                 device = hwNode("usb");
00424               device.setHandle(usbhandle(bus, lev, devnum));
00425               device.setBusInfo(usbbusinfo(bus, lev, port));
00426               device.setPhysId(port+1);
00427               device.setConfig("speed", usbspeed(speed));
00428               if(mxch>0)
00429               {
00430                 snprintf(strval, sizeof(strval), "%u", mxch);
00431                 device.setConfig("slots", strval);
00432               }
00433           }
00434           break;
00435           case 'D':
00436             strcpy(ver, "");
00437             cls = sub = prot = mxps = numcfgs = 0;
00438             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)
00439             {
00440               setUSBClass(device, cls, sub, prot);
00441               device.addCapability(string("usb-")+string(ver));
00442               device.describeCapability("usb-1.00", "USB 1.0");
00443               device.describeCapability("usb-1.10", "USB 1.1");
00444               device.describeCapability("usb-2.00", "USB 2.0");
00445               device.addHint("usb.bDeviceClass", cls);
00446               device.addHint("usb.bDeviceSubClass", sub);
00447               device.addHint("usb.bDeviceProtocol", prot);
00448             }
00449             break;
00450           case 'P':
00451             vendor = prodid = 0;
00452             strcpy(rev, "");
00453             if(sscanf(line.c_str(), "P: Vendor=%x ProdID=%x Rev=%10s", &vendor, &prodid, rev)>0)
00454             {
00455               describeUSB(device, vendor, prodid);
00456               device.setVersion(hw::strip(rev));
00457             }
00458             break;
00459           case 'S':
00460             memset(strname, 0, sizeof(strname));
00461             memset(strval, 0, sizeof(strval));
00462             if(sscanf(line.c_str(), "S: %80[^=]=%80[ -z]", strname, strval)>0)
00463             {
00464               if(strcasecmp(strname, "Manufacturer")==0)
00465                 device.setVendor(hw::strip(strval)+(enabled("output:numeric")?" ["+tohex(vendor)+"]":""));
00466               if(strcasecmp(strname, "Product")==0)
00467                 device.setProduct(hw::strip(strval)+(enabled("output:numeric")?" ["+tohex(vendor)+":"+tohex(prodid)+"]":""));
00468               if(strcasecmp(strname, "SerialNumber")==0)
00469                 device.setSerial(hw::strip(strval));
00470             }
00471             break;
00472           case 'C':
00473             numifs = cfgnum = atr = 0;
00474             strcpy(mxpwr, "");
00475             if(sscanf(line.c_str(), "C:* #Ifs=%u Cfg#=%u Atr=%x MxPwr=%s", &numifs, &cfgnum, &atr, mxpwr)>0)
00476             {
00477               if(strcmp("0mA", mxpwr)!=0)
00478                 device.setConfig("maxpower", mxpwr);
00479             }
00480             break;
00481           case 'I':
00482             ifnum = alt = numeps = cls = sub = prot = 0;
00483             memset(driver, 0, sizeof(driver));
00484             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)))
00485             {
00486               setUSBClass(device, cls, sub, prot);
00487               if((strlen(driver)!=0) && (strcasecmp("(none)", driver)!=0))
00488               {
00489                 device.setConfig("driver", hw::strip(driver));
00490                 device.claim();
00491               }
00492             }
00493             break;
00494         }
00495       }
00496     }
00497   }
00498   if(defined)
00499     addUSBChild(n, device, bus, lev, prnt);
00500 
00501   if(usbdevices) fclose(usbdevices);
00502 
00503   return true;
00504 }