diff options
Diffstat (limited to 'kcontrol/usbview/usbdevices.cpp')
-rw-r--r-- | kcontrol/usbview/usbdevices.cpp | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/kcontrol/usbview/usbdevices.cpp b/kcontrol/usbview/usbdevices.cpp new file mode 100644 index 000000000..3d557a7ed --- /dev/null +++ b/kcontrol/usbview/usbdevices.cpp @@ -0,0 +1,431 @@ +/*************************************************************************** + * Copyright (C) 2001 by Matthias Hoelzer-Kluepfel <mhk@caldera.de> * + * * + * 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 <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> + +#include <qfile.h> +#include <qdir.h> +#include <qregexp.h> + +#include <klocale.h> +#include <kmessagebox.h> + +#include "usbdb.h" +#include "usbdevices.h" + +#include <math.h> + +#ifdef Q_OS_FREEBSD +#include <sys/ioctl.h> +#include <sys/param.h> +#endif + +QPtrList<USBDevice> 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<USBDevice> 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 = "<qml><h2><center>" + product() + "</center></h2><br/><hl/>"; + + if (!_manufacturer.isEmpty()) + r += i18n("<b>Manufacturer:</b> ") + _manufacturer + "<br/>"; + if (!_serial.isEmpty()) + r += i18n("<b>Serial #:</b> ") + _serial + "<br/>"; + + r += "<br/><table>"; + + QString c = QString("<td>%1</td>").arg(_class); + QString cname = _db->cls(_class); + if (!cname.isEmpty()) + c += "<td>(" + i18n(cname.latin1()) +")</td>"; + r += i18n("<tr><td><i>Class</i></td>%1</tr>").arg(c); + QString sc = QString("<td>%1</td>").arg(_sub); + QString scname = _db->subclass(_class, _sub); + if (!scname.isEmpty()) + sc += "<td>(" + i18n(scname.latin1()) +")</td>"; + r += i18n("<tr><td><i>Subclass</i></td>%1</tr>").arg(sc); + QString pr = QString("<td>%1</td>").arg(_prot); + QString prname = _db->protocol(_class, _sub, _prot); + if (!prname.isEmpty()) + pr += "<td>(" + prname +")</td>"; + r += i18n("<tr><td><i>Protocol</i></td>%1</tr>").arg(pr); +#ifndef Q_OS_FREEBSD + r += i18n("<tr><td><i>USB Version</i></td><td>%1.%2</td></tr>") + .arg(_verMajor,0,16) + .arg(QString::number(_verMinor,16).prepend('0').right(2)); +#endif + r += "<tr><td></td></tr>"; + + QString v = QString::number(_vendorID,16); + QString name = _db->vendor(_vendorID); + if (!name.isEmpty()) + v += "<td>(" + name +")</td>"; + r += i18n("<tr><td><i>Vendor ID</i></td><td>0x%1</td></tr>").arg(v); + QString p = QString::number(_prodID,16); + QString pname = _db->device(_vendorID, _prodID); + if (!pname.isEmpty()) + p += "<td>(" + pname +")</td>"; + r += i18n("<tr><td><i>Product ID</i></td><td>0x%1</td></tr>").arg(p); + r += i18n("<tr><td><i>Revision</i></td><td>%1.%2</td></tr>") + .arg(_revMajor,0,16) + .arg(QString::number(_revMinor,16).prepend('0').right(2)); + r += "<tr><td></td></tr>"; + + r += i18n("<tr><td><i>Speed</i></td><td>%1 Mbit/s</td></tr>").arg(_speed); + r += i18n("<tr><td><i>Channels</i></td><td>%1</td></tr>").arg(_channels); +#ifdef Q_OS_FREEBSD + if ( _power ) + r += i18n("<tr><td><i>Power Consumption</i></td><td>%1 mA</td></tr>").arg(_power); + else + r += i18n("<tr><td><i>Power Consumption</i></td><td>self powered</td></tr>"); + r += i18n("<tr><td><i>Attached Devicenodes</i></td><td>%1</td></tr>").arg(*_devnodes.at(0)); + if ( _devnodes.count() > 1 ) + for ( QStringList::Iterator it = _devnodes.at(1); it != _devnodes.end(); ++it ) + r += "<tr><td></td><td>" + *it + "</td></tr>"; +#else + r += i18n("<tr><td><i>Max. Packet Size</i></td><td>%1</td></tr>").arg(_maxPacketSize); +#endif + r += "<tr><td></td></tr>"; + + if (_hasBW) + { + r += i18n("<tr><td><i>Bandwidth</i></td><td>%1 of %2 (%3%)</td></tr>").arg(_bwUsed).arg(_bwTotal).arg(_bwPercent); + r += i18n("<tr><td><i>Intr. requests</i></td><td>%1</td></tr>").arg(_bwIntr); + r += i18n("<tr><td><i>Isochr. requests</i></td><td>%1</td></tr>").arg(_bwIso); + r += "<tr><td></td></tr>"; + } + + r += "</table>"; + + 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 <markus@brueffer.de> + * + * 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 |