1
/***************************************************************************
2
* Copyright (C) 2001 by Matthias Hoelzer-Kluepfel <mhk@caldera.de> *
4
* This program is free software; you can redistribute it and/or modify *
5
* it under the terms of the GNU General Public License as published by *
6
* the Free Software Foundation; either version 2 of the License, or *
7
* (at your option) any later version. *
9
***************************************************************************/
11
#include "usbdevices.h"
13
#include <sys/types.h>
24
#include <kmessagebox.h>
30
#if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
31
#include <sys/ioctl.h>
32
#include <sys/param.h>
35
QList<USBDevice*> USBDevice::_devices;
36
USBDB *USBDevice::_db;
38
USBDevice::USBDevice() :
39
_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),
40
_prodID(0), _revMajor(0), _revMinor(0) {
41
_devices.append(this);
47
USBDevice::~USBDevice() {
51
static QString catFile(QString fname) {
54
int fd =:: open(QFile::encodeName(fname), O_RDONLY);
61
while ((count = ::read(fd, buffer, 256)) > 0)
62
result.append(QString(buffer).left(count));
66
return result.trimmed();
69
void USBDevice::parseSysDir(int bus, int parent, int level, const QString& dname) {
72
_manufacturer = catFile(dname + "/manufacturer");
73
_product = catFile(dname + "/product");
76
_device = catFile(dname + "/devnum").toUInt();
79
_product += QString(" (%1)").arg(_bus);
81
_vendorID = catFile(dname + "/idVendor").toUInt(0, 16);
82
_prodID = catFile(dname + "/idProduct").toUInt(0, 16);
84
_class = catFile(dname + "/bDeviceClass").toUInt(0, 16);
85
_sub = catFile(dname + "/bDeviceSubClass").toUInt(0, 16);
86
_maxPacketSize = catFile(dname + "/bMaxPacketSize0").toUInt();
88
_speed = catFile(dname + "/speed").toDouble();
89
_serial = catFile(dname + "/serial");
90
_channels = catFile(dname + "/maxchild").toUInt();
92
double version = catFile(dname + "/version").toDouble();
93
_verMajor = int(version);
94
_verMinor = int(10*(version - floor(version)));
97
dir.setNameFilters(QStringList() << QString("%1-*").arg(bus));
98
dir.setFilter(QDir::Dirs);
99
const QStringList list = dir.entryList();
101
for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) {
102
if ((*it).contains(':'))
105
USBDevice* dev = new USBDevice();
106
dev->parseSysDir(bus, ++level, _device, dname + '/' + *it);
110
void USBDevice::parseLine(const QString& line) {
111
if (line.startsWith("T:"))
112
sscanf(line.toLocal8Bit().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);
113
else if (line.startsWith("S: Manufacturer"))
114
_manufacturer = line.mid(17);
115
else if (line.startsWith("S: Product")) {
116
_product = line.mid(12);
117
/* add bus number to root devices */
119
_product += QString(" (%1)").arg(_bus);
120
} else if (line.startsWith("S: SerialNumber"))
121
_serial = line.mid(17);
122
else if (line.startsWith("B:")) {
123
sscanf(line.toLocal8Bit().data(), "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d", &_bwUsed, &_bwTotal, &_bwPercent, &_bwIntr, &_bwIso);
125
} else if (line.startsWith("D:")) {
127
sscanf(line.toLocal8Bit().data(), "D: Ver=%x.%x Cls=%x(%10s) Sub=%x Prot=%x MxPS=%u #Cfgs=%u", &_verMajor, &_verMinor, &_class, buffer, &_sub, &_prot, &_maxPacketSize, &_configs);
129
} else if (line.startsWith("P:"))
130
sscanf(line.toLocal8Bit().data(), "P: Vendor=%x ProdID=%x Rev=%x.%x", &_vendorID, &_prodID, &_revMajor, &_revMinor);
133
USBDevice* USBDevice::find(int bus, int device) {
134
foreach(USBDevice* usbDevice, _devices) {
135
if (usbDevice->bus() == bus && usbDevice->device() == device)
142
QString USBDevice::product() {
143
if (!_product.isEmpty())
145
QString pname = _db->device(_vendorID, _prodID);
146
if (!pname.isEmpty())
148
return i18n("Unknown");
151
QString USBDevice::dump() {
154
r = "<qml><h2><center>" + product() + "</center></h2><br/><hl/>";
156
if (!_manufacturer.isEmpty())
157
r += i18n("<b>Manufacturer:</b> ") + _manufacturer + "<br/>";
158
if (!_serial.isEmpty())
159
r += i18n("<b>Serial #:</b> ") + _serial + "<br/>";
163
QString c = QString("<td>%1</td>").arg(_class);
164
QString cname = _db->cls(_class);
165
if (!cname.isEmpty())
166
c += "<td>(" + i18n(cname.toLatin1()) +")</td>";
167
r += i18n("<tr><td><i>Class</i></td>%1</tr>", c);
168
QString sc = QString("<td>%1</td>").arg(_sub);
169
QString scname = _db->subclass(_class, _sub);
170
if (!scname.isEmpty())
171
sc += "<td>(" + i18n(scname.toLatin1()) +")</td>";
172
r += i18n("<tr><td><i>Subclass</i></td>%1</tr>", sc);
173
QString pr = QString("<td>%1</td>").arg(_prot);
174
QString prname = _db->protocol(_class, _sub, _prot);
175
if (!prname.isEmpty())
176
pr += "<td>(" + prname +")</td>";
177
r += i18n("<tr><td><i>Protocol</i></td>%1</tr>", pr);
178
#if !(defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD))
179
r += ki18n("<tr><td><i>USB Version</i></td><td>%1.%2</td></tr>")
180
.subs(_verMajor,0,16).subs(_verMinor,2,16,QChar::fromLatin1('0'))
183
r += "<tr><td></td></tr>";
185
QString v = QString::number(_vendorID, 16);
186
QString name = _db->vendor(_vendorID);
188
v += "<td>(" + name +")</td>";
189
r += i18n("<tr><td><i>Vendor ID</i></td><td>0x%1</td></tr>", v);
190
QString p = QString::number(_prodID, 16);
191
QString pname = _db->device(_vendorID, _prodID);
192
if (!pname.isEmpty())
193
p += "<td>(" + pname +")</td>";
194
r += i18n("<tr><td><i>Product ID</i></td><td>0x%1</td></tr>", p);
195
r += ki18n("<tr><td><i>Revision</i></td><td>%1.%2</td></tr>")
196
.subs(_revMajor,0,16).subs(_revMinor,2,16,QChar::fromLatin1('0'))
198
r += "<tr><td></td></tr>";
200
r += i18n("<tr><td><i>Speed</i></td><td>%1 Mbit/s</td></tr>", _speed);
201
r += i18n("<tr><td><i>Channels</i></td><td>%1</td></tr>", _channels);
202
#if (defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)) && !defined(DISABLE_USBDEVICES_FREEBSD)
204
r += i18n("<tr><td><i>Power Consumption</i></td><td>%1 mA</td></tr>", _power);
206
r += i18n("<tr><td><i>Power Consumption</i></td><td>self powered</td></tr>");
207
r += i18n("<tr><td><i>Attached Devicenodes</i></td><td>%1</td></tr>", _devnodes.at(0));
208
if ( _devnodes.count() > 1 ) {
209
QStringList::const_iterator it = _devnodes.constBegin();
211
for (; it != _devnodes.constEnd(); ++it )
212
r += "<tr><td></td><td>" + *it + "</td></tr>";
215
r += i18n("<tr><td><i>Max. Packet Size</i></td><td>%1</td></tr>", _maxPacketSize);
217
r += "<tr><td></td></tr>";
220
r += i18n("<tr><td><i>Bandwidth</i></td><td>%1 of %2 (%3%)</td></tr>", _bwUsed, _bwTotal, _bwPercent);
221
r += i18n("<tr><td><i>Intr. requests</i></td><td>%1</td></tr>", _bwIntr);
222
r += i18n("<tr><td><i>Isochr. requests</i></td><td>%1</td></tr>", _bwIso);
223
r += "<tr><td></td></tr>";
231
#if !(defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD))
232
bool USBDevice::parse(const QString &fname) {
237
// read in the complete file
239
// Note: we can't use a QTextStream, as the files in /proc
240
// are pseudo files with zero length
242
int fd =:: open(QFile::encodeName(fname), O_RDONLY);
249
while ((count = ::read(fd, buffer, 256)) > 0)
250
result.append(QString(buffer).left(count));
255
// read in the device infos
256
USBDevice *device = 0;
258
result.replace(QRegExp("^\n"),"");
259
while ((end = result.indexOf('\n', start)) > 0)
261
QString line = result.mid(start, end-start);
263
if (line.startsWith("T:"))
264
device = new USBDevice();
267
device->parseLine(line);
274
bool USBDevice::parseSys(const QString &dname) {
276
d.setNameFilters(QStringList() << "usb*");
277
const QStringList list = d.entryList();
279
for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) {
280
USBDevice* device = new USBDevice();
283
QRegExp bus_reg("[a-z]*([0-9]+)");
284
if (bus_reg.indexIn(*it) != -1)
285
bus = bus_reg.cap(1).toInt();
287
device->parseSysDir(bus, 0, 0, d.absolutePath() + '/' + *it);
296
bool USBDevice::parseSys(const QString &fname)
303
# if defined(DISABLE_USBDEVICES_FREEBSD)
306
* FIXME: The USB subsystem has changed a lot in FreeBSD 8.0
307
* Support for it must be written.
310
bool USBDevice::parse(const QString &fname)
320
* FreeBSD support by Markus Brueffer <markus@brueffer.de>
322
* Basic idea and some code fragments were taken from FreeBSD's usbdevs(8),
323
* originally developed for NetBSD, so this code should work with no or
324
* only little modification on NetBSD.
327
void USBDevice::collectData( int fd, int level, usb_device_info &di, int parent)
329
// determine data for this device
334
_device = di.udi_addr;
335
_product = QLatin1String(di.udi_product);
337
_product += ' ' + QString::number( _bus );
338
_manufacturer = QLatin1String(di.udi_vendor);
339
_prodID = di.udi_productNo;
340
_vendorID = di.udi_vendorNo;
341
_class = di.udi_class;
342
_sub = di.udi_subclass;
343
_prot = di.udi_protocol;
344
_power = di.udi_power;
345
_channels = di.udi_nports;
347
// determine the speed
348
#if defined(__DragonFly__) || (defined(Q_OS_FREEBSD) && __FreeBSD_version > 490102) || defined(Q_OS_NETBSD)
349
switch (di.udi_speed) {
350
case USB_SPEED_LOW: _speed = 1.5; break;
351
case USB_SPEED_FULL: _speed = 12.0; break;
352
case USB_SPEED_HIGH: _speed = 480.0; break;
355
_speed = di.udi_lowspeed ? 1.5 : 12.0;
358
// Get all attached devicenodes
359
for ( int i = 0; i < USB_MAX_DEVNAMES; ++i )
360
if ( di.udi_devnames[i][0] )
361
_devnodes << di.udi_devnames[i];
363
// For compatibility, split the revision number
364
sscanf( di.udi_release, "%x.%x", &_revMajor, &_revMinor );
366
// Cycle through the attached devices if there are any
367
for ( int p = 0; p < di.udi_nports; ++p ) {
368
// Get data for device
369
struct usb_device_info di2;
371
di2.udi_addr = di.udi_ports[p];
373
if ( di2.udi_addr >= USB_MAX_DEVICES )
376
if ( ioctl(fd, USB_DEVICEINFO, &di2) == -1 )
379
// Only add the device if we didn't detect it, yet
380
if (!find( di2.udi_bus, di2.udi_addr ) )
382
USBDevice *device = new USBDevice();
383
device->collectData( fd, level + 1, di2, di.udi_addr );
388
bool USBDevice::parse(const QString &fname)
392
static bool showErrorMessage = true;
396
QFile controller("/dev/usb0");
398
while ( controller.exists() )
400
// If the devicenode exists, continue with further inspection
401
if ( controller.open(QIODevice::ReadOnly) )
403
for ( int addr = 1; addr < USB_MAX_DEVICES; ++addr )
405
struct usb_device_info di;
408
if ( ioctl(controller.handle(), USB_DEVICEINFO, &di) != -1 )
410
if (!find( di.udi_bus, di.udi_addr ) )
412
USBDevice *device = new USBDevice();
413
device->collectData( controller.handle(), 0, di, 0);
423
controller.setFileName( QString::fromLocal8Bit("/dev/usb%1").arg(i++) );
426
if ( showErrorMessage && error ) {
427
showErrorMessage = false;
428
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."));
434
# endif // defined(DISABLE_USBDEVICES_FREEBSD)
435
#endif // !(defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD))