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)
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 * FreeBSD support by Markus Brueffer <markus@brueffer.de>
298 * Basic idea and some code fragments were taken from FreeBSD's usbdevs(8),
299 * originally developed for NetBSD, so this code should work with no or
300 * only little modification on NetBSD.
303 void USBDevice::collectData( int fd
, int level
, usb_device_info
&di
, int parent
)
305 // determine data for this device
310 _device
= di
.udi_addr
;
311 _product
= QLatin1String(di
.udi_product
);
313 _product
+= ' ' + QString::number( _bus
);
314 _manufacturer
= QLatin1String(di
.udi_vendor
);
315 _prodID
= di
.udi_productNo
;
316 _vendorID
= di
.udi_vendorNo
;
317 _class
= di
.udi_class
;
318 _sub
= di
.udi_subclass
;
319 _prot
= di
.udi_protocol
;
320 _power
= di
.udi_power
;
321 _channels
= di
.udi_nports
;
323 // determine the speed
324 #if defined(__DragonFly__) || (defined(Q_OS_FREEBSD) && __FreeBSD_version > 490102) || defined(Q_OS_NETBSD)
325 switch (di
.udi_speed
) {
326 case USB_SPEED_LOW
: _speed
= 1.5; break;
327 case USB_SPEED_FULL
: _speed
= 12.0; break;
328 case USB_SPEED_HIGH
: _speed
= 480.0; break;
331 _speed
= di
.udi_lowspeed
? 1.5 : 12.0;
334 // Get all attached devicenodes
335 for ( int i
= 0; i
< USB_MAX_DEVNAMES
; ++i
)
336 if ( di
.udi_devnames
[i
][0] )
337 _devnodes
<< di
.udi_devnames
[i
];
339 // For compatibility, split the revision number
340 sscanf( di
.udi_release
, "%x.%x", &_revMajor
, &_revMinor
);
342 // Cycle through the attached devices if there are any
343 for ( int p
= 0; p
< di
.udi_nports
; ++p
) {
344 // Get data for device
345 struct usb_device_info di2
;
347 di2
.udi_addr
= di
.udi_ports
[p
];
349 if ( di2
.udi_addr
>= USB_MAX_DEVICES
)
352 if ( ioctl(fd
, USB_DEVICEINFO
, &di2
) == -1 )
355 // Only add the device if we didn't detect it, yet
356 if (!find( di2
.udi_bus
, di2
.udi_addr
) )
358 USBDevice
*device
= new USBDevice();
359 device
->collectData( fd
, level
+ 1, di2
, di
.udi_addr
);
364 bool USBDevice::parse(const QString
&fname
)
366 static bool showErrorMessage
= true;
370 QFile
controller("/dev/usb0");
372 while ( controller
.exists() )
374 // If the devicenode exists, continue with further inspection
375 if ( controller
.open(QIODevice::ReadOnly
) )
377 for ( int addr
= 1; addr
< USB_MAX_DEVICES
; ++addr
)
379 struct usb_device_info di
;
382 if ( ioctl(controller
.handle(), USB_DEVICEINFO
, &di
) != -1 )
384 if (!find( di
.udi_bus
, di
.udi_addr
) )
386 USBDevice
*device
= new USBDevice();
387 device
->collectData( controller
.handle(), 0, di
, 0);
397 controller
.setFileName( QString::fromLocal8Bit("/dev/usb%1").arg(i
++) );
400 if ( showErrorMessage
&& error
) {
401 showErrorMessage
= false;
402 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."));