/*************************************************************************** * Copyright (C) 2001 by Matthias Hoelzer-Kluepfel * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "usbdb.h" #include "usbdevices.h" #include #ifdef Q_OS_FREEBSD #include #include #endif QPtrList USBDevice::_devices; USBDB *USBDevice::_db; USBDevice::USBDevice() : _bus(0), _level(0), _parent(0), _port(0), _count(0), _device(0), _channels(0), _power(0), _speed(0.0), _bwTotal(0), _bwUsed(0), _bwPercent(0), _bwIntr(0), _bwIso(0), _hasBW(false), _verMajor(0), _verMinor(0), _class(0), _sub(0), _prot(0), _maxPacketSize(0), _configs(0), _vendorID(0), _prodID(0), _revMajor(0), _revMinor(0) { _devices.append(this); _devices.setAutoDelete(true); if (!_db) _db = new USBDB; } static QString catFile(QString fname) { char buffer[256]; QString result; int fd = ::open(QFile::encodeName(fname), O_RDONLY); if (fd<0) return QString::null; if (fd >= 0) { ssize_t count; while ((count = ::read(fd, buffer, 256)) > 0) result.append(QString(buffer).left(count)); ::close(fd); } return result.stripWhiteSpace(); } void USBDevice::parseSysDir(int bus, int parent, int level, QString dname) { _level = level; _parent = parent; _manufacturer = catFile(dname + "/manufacturer"); _product = catFile(dname + "/product"); _bus = bus; _device = catFile(dname + "/devnum").toUInt(); if (_device == 1) _product += QString(" (%1)").arg(_bus); _vendorID = catFile(dname + "/idVendor").toUInt(0, 16); _prodID = catFile(dname + "/idProduct").toUInt(0, 16); _class = catFile(dname + "/bDeviceClass").toUInt(0, 16); _sub = catFile(dname + "/bDeviceSubClass").toUInt(0, 16); _maxPacketSize = catFile(dname + "/bMaxPacketSize0").toUInt(); _speed = catFile(dname + "/speed").toDouble(); _serial = catFile(dname + "/serial"); _channels = catFile(dname + "/maxchild").toUInt(); double version = catFile(dname + "/version").toDouble(); _verMajor = int(version); _verMinor = int(10*(version - floor(version))); QDir dir(dname); dir.setNameFilter(QString("%1-*").arg(bus)); dir.setFilter(QDir::Dirs); QStringList list = dir.entryList(); for(QStringList::Iterator it = list.begin(); it != list.end(); ++it) { if ((*it).contains(':')) continue; USBDevice* dev = new USBDevice(); dev->parseSysDir(bus, ++level, _device, dname + "/" + *it); } } void USBDevice::parseLine(QString line) { if (line.startsWith("T:")) sscanf(line.local8Bit().data(), "T: Bus=%2d Lev=%2d Prnt=%2d Port=%d Cnt=%2d Dev#=%3d Spd=%3f MxCh=%2d", &_bus, &_level, &_parent, &_port, &_count, &_device, &_speed, &_channels); else if (line.startsWith("S: Manufacturer")) _manufacturer = line.mid(17); else if (line.startsWith("S: Product")) { _product = line.mid(12); /* add bus number to root devices */ if (_device==1) _product += QString(" (%1)").arg(_bus); } else if (line.startsWith("S: SerialNumber")) _serial = line.mid(17); else if (line.startsWith("B:")) { sscanf(line.local8Bit().data(), "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d", &_bwUsed, &_bwTotal, &_bwPercent, &_bwIntr, &_bwIso); _hasBW = true; } else if (line.startsWith("D:")) { char buffer[11]; sscanf(line.local8Bit().data(), "D: Ver=%x.%x Cls=%x(%10s) Sub=%x Prot=%x MxPS=%d #Cfgs=%d", &_verMajor, &_verMinor, &_class, buffer, &_sub, &_prot, &_maxPacketSize, &_configs); _className = buffer; } else if (line.startsWith("P:")) sscanf(line.local8Bit().data(), "P: Vendor=%x ProdID=%x Rev=%x.%x", &_vendorID, &_prodID, &_revMajor, &_revMinor); } USBDevice *USBDevice::find(int bus, int device) { QPtrListIterator it(_devices); for ( ; it.current(); ++it) if (it.current()->bus() == bus && it.current()->device() == device) return it.current(); return 0; } QString USBDevice::product() { if (!_product.isEmpty()) return _product; QString pname = _db->device(_vendorID, _prodID); if (!pname.isEmpty()) return pname; return i18n("Unknown"); } QString USBDevice::dump() { QString r; r = "

" + product() + "


"; if (!_manufacturer.isEmpty()) r += i18n("Manufacturer: ") + _manufacturer + "
"; if (!_serial.isEmpty()) r += i18n("Serial #: ") + _serial + "
"; r += "
"; QString c = QString("").arg(_class); QString cname = _db->cls(_class); if (!cname.isEmpty()) c += ""; r += i18n("%1").arg(c); QString sc = QString("").arg(_sub); QString scname = _db->subclass(_class, _sub); if (!scname.isEmpty()) sc += ""; r += i18n("%1").arg(sc); QString pr = QString("").arg(_prot); QString prname = _db->protocol(_class, _sub, _prot); if (!prname.isEmpty()) pr += ""; r += i18n("%1").arg(pr); #ifndef Q_OS_FREEBSD r += i18n("") .arg(_verMajor,0,16) .arg(QString::number(_verMinor,16).prepend('0').right(2)); #endif r += ""; QString v = QString::number(_vendorID,16); QString name = _db->vendor(_vendorID); if (!name.isEmpty()) v += ""; r += i18n("").arg(v); QString p = QString::number(_prodID,16); QString pname = _db->device(_vendorID, _prodID); if (!pname.isEmpty()) p += ""; r += i18n("").arg(p); r += i18n("") .arg(_revMajor,0,16) .arg(QString::number(_revMinor,16).prepend('0').right(2)); r += ""; r += i18n("").arg(_speed); r += i18n("").arg(_channels); #ifdef Q_OS_FREEBSD if ( _power ) r += i18n("").arg(_power); else r += i18n(""); r += i18n("").arg(*_devnodes.at(0)); if ( _devnodes.count() > 1 ) for ( QStringList::Iterator it = _devnodes.at(1); it != _devnodes.end(); ++it ) r += ""; #else r += i18n("").arg(_maxPacketSize); #endif r += ""; if (_hasBW) { r += i18n("").arg(_bwUsed).arg(_bwTotal).arg(_bwPercent); r += i18n("").arg(_bwIntr); r += i18n("").arg(_bwIso); r += ""; } r += "
%1(" + i18n(cname.latin1()) +")
Class
%1(" + i18n(scname.latin1()) +")
Subclass
%1(" + prname +")
Protocol
USB Version%1.%2
(" + name +")
Vendor ID0x%1
(" + pname +")
Product ID0x%1
Revision%1.%2
Speed%1 Mbit/s
Channels%1
Power Consumption%1 mA
Power Consumptionself powered
Attached Devicenodes%1
" + *it + "
Max. Packet Size%1
Bandwidth%1 of %2 (%3%)
Intr. requests%1
Isochr. requests%1
"; return r; } #ifndef Q_OS_FREEBSD bool USBDevice::parse(QString fname) { _devices.clear(); QString result; // read in the complete file // // Note: we can't use a QTextStream, as the files in /proc // are pseudo files with zero length char buffer[256]; int fd = ::open(QFile::encodeName(fname), O_RDONLY); if (fd<0) return false; if (fd >= 0) { ssize_t count; while ((count = ::read(fd, buffer, 256)) > 0) result.append(QString(buffer).left(count)); ::close(fd); } // read in the device infos USBDevice *device = 0; int start=0, end; result.replace(QRegExp("^\n"),""); while ((end = result.find('\n', start)) > 0) { QString line = result.mid(start, end-start); if (line.startsWith("T:")) device = new USBDevice(); if (device) device->parseLine(line); start = end+1; } return true; } bool USBDevice::parseSys(QString dname) { QDir d(dname); d.setNameFilter("usb*"); QStringList list = d.entryList(); for(QStringList::Iterator it = list.begin(); it != list.end(); ++it) { USBDevice* device = new USBDevice(); int bus = 0; QRegExp bus_reg("[a-z]*([0-9]+)"); if (bus_reg.search(*it) != -1) bus = bus_reg.cap(1).toInt(); device->parseSysDir(bus, 0, 0, d.absPath() + "/" + *it); } return d.count(); } #else /* * FreeBSD support by Markus Brueffer * * Basic idea and some code fragments were taken from FreeBSD's usbdevs(8), * originally developed for NetBSD, so this code should work with no or * only little modification on NetBSD. */ void USBDevice::collectData( int fd, int level, usb_device_info &di, int parent) { // determine data for this device _level = level; _parent = parent; _bus = di.udi_bus; _device = di.udi_addr; _product = QString::fromLatin1(di.udi_product); if ( _device == 1 ) _product += " " + QString::number( _bus ); _manufacturer = QString::fromLatin1(di.udi_vendor); _prodID = di.udi_productNo; _vendorID = di.udi_vendorNo; _class = di.udi_class; _sub = di.udi_subclass; _prot = di.udi_protocol; _power = di.udi_power; _channels = di.udi_nports; // determine the speed #if __FreeBSD_version > 490102 switch (di.udi_speed) { case USB_SPEED_LOW: _speed = 1.5; break; case USB_SPEED_FULL: _speed = 12.0; break; case USB_SPEED_HIGH: _speed = 480.0; break; } #else _speed = di.udi_lowspeed ? 1.5 : 12.0; #endif // Get all attached devicenodes for ( int i = 0; i < USB_MAX_DEVNAMES; ++i ) if ( di.udi_devnames[i][0] ) _devnodes << di.udi_devnames[i]; // For compatibility, split the revision number sscanf( di.udi_release, "%x.%x", &_revMajor, &_revMinor ); // Cycle through the attached devices if there are any for ( int p = 0; p < di.udi_nports; ++p ) { // Get data for device struct usb_device_info di2; di2.udi_addr = di.udi_ports[p]; if ( di2.udi_addr >= USB_MAX_DEVICES ) continue; if ( ioctl(fd, USB_DEVICEINFO, &di2) == -1 ) continue; // Only add the device if we didn't detect it, yet if (!find( di2.udi_bus, di2.udi_addr ) ) { USBDevice *device = new USBDevice(); device->collectData( fd, level + 1, di2, di.udi_addr ); } } } bool USBDevice::parse(QString fname) { static bool showErrorMessage = true; bool error = false; _devices.clear(); QFile controller("/dev/usb0"); int i = 1; while ( controller.exists() ) { // If the devicenode exists, continue with further inspection if ( controller.open(IO_ReadOnly) ) { for ( int addr = 1; addr < USB_MAX_DEVICES; ++addr ) { struct usb_device_info di; di.udi_addr = addr; if ( ioctl(controller.handle(), USB_DEVICEINFO, &di) != -1 ) { if (!find( di.udi_bus, di.udi_addr ) ) { USBDevice *device = new USBDevice(); device->collectData( controller.handle(), 0, di, 0); } } } controller.close(); } else { error = true; } controller.setName( QString::fromLocal8Bit("/dev/usb%1").arg(i++) ); } if ( showErrorMessage && error ) { showErrorMessage = false; KMessageBox::error( 0, i18n("Could not open one or more USB controller. Make sure, you have read access to all USB controllers that should be listed here.")); } return true; } #endif