Fix crash if key bindings specified in profile cannot be found. Improve
[personal-kdebase.git] / apps / kinfocenter / usbview / usbdevices.cpp
blob07889c6c0cfc090f8b64dcddcfceefb906177cfb
1 /***************************************************************************
2 * Copyright (C) 2001 by Matthias Hoelzer-Kluepfel <mhk@caldera.de> *
3 * *
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. *
8 * *
9 ***************************************************************************/
11 #include "usbdevices.h"
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <stdio.h>
19 #include <QFile>
20 #include <QDir>
21 #include <QRegExp>
23 #include <klocale.h>
24 #include <kmessagebox.h>
26 #include "usbdb.h"
28 #include <math.h>
30 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
31 #include <sys/ioctl.h>
32 #include <sys/param.h>
33 #endif
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);
43 if (!_db)
44 _db = new USBDB;
47 USBDevice::~USBDevice() {
51 static QString catFile(QString fname) {
52 char buffer[256];
53 QString result;
54 int fd =:: open(QFile::encodeName(fname), O_RDONLY);
55 if (fd<0)
56 return QString();
58 if (fd >= 0)
60 ssize_t count;
61 while ((count = ::read(fd, buffer, 256)) > 0)
62 result.append(QString(buffer).left(count));
64 ::close(fd);
66 return result.trimmed();
69 void USBDevice::parseSysDir(int bus, int parent, int level, const QString& dname) {
70 _level = level;
71 _parent = parent;
72 _manufacturer = catFile(dname + "/manufacturer");
73 _product = catFile(dname + "/product");
75 _bus = bus;
76 _device = catFile(dname + "/devnum").toUInt();
78 if (_device == 1)
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)));
96 QDir dir(dname);
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(':'))
103 continue;
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 */
118 if (_device==1)
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);
124 _hasBW = true;
125 } else if (line.startsWith("D:")) {
126 char buffer[11];
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);
128 _className = buffer;
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)
136 return usbDevice;
139 return NULL;
142 QString USBDevice::product() {
143 if (!_product.isEmpty())
144 return _product;
145 QString pname = _db->device(_vendorID, _prodID);
146 if (!pname.isEmpty())
147 return pname;
148 return i18n("Unknown");
151 QString USBDevice::dump() {
152 QString r;
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/>";
161 r += "<br/><table>";
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'))
181 .toString();
182 #endif
183 r += "<tr><td></td></tr>";
185 QString v = QString::number(_vendorID, 16);
186 QString name = _db->vendor(_vendorID);
187 if (!name.isEmpty())
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'))
197 .toString();
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)
203 if ( _power )
204 r += i18n("<tr><td><i>Power Consumption</i></td><td>%1 mA</td></tr>", _power);
205 else
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();
210 ++it;
211 for (; it != _devnodes.constEnd(); ++it )
212 r += "<tr><td></td><td>" + *it + "</td></tr>";
214 #else
215 r += i18n("<tr><td><i>Max. Packet Size</i></td><td>%1</td></tr>", _maxPacketSize);
216 #endif
217 r += "<tr><td></td></tr>";
219 if (_hasBW) {
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>";
226 r += "</table>";
228 return r;
231 #if !(defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD))
232 bool USBDevice::parse(const QString &fname) {
233 _devices.clear();
235 QString result;
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
241 char buffer[256];
242 int fd =:: open(QFile::encodeName(fname), O_RDONLY);
243 if (fd<0)
244 return false;
246 if (fd >= 0)
248 ssize_t count;
249 while ((count = ::read(fd, buffer, 256)) > 0)
250 result.append(QString(buffer).left(count));
252 ::close(fd);
255 // read in the device infos
256 USBDevice *device = 0;
257 int start=0, end;
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();
266 if (device)
267 device->parseLine(line);
269 start = end+1;
271 return true;
274 bool USBDevice::parseSys(const QString &dname) {
275 QDir d(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();
282 int bus = 0;
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);
290 return d.count();
293 #else
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
306 _level = level;
307 _parent = parent;
309 _bus = di.udi_bus;
310 _device = di.udi_addr;
311 _product = QLatin1String(di.udi_product);
312 if ( _device == 1 )
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;
330 #else
331 _speed = di.udi_lowspeed ? 1.5 : 12.0;
332 #endif
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 )
350 continue;
352 if ( ioctl(fd, USB_DEVICEINFO, &di2) == -1 )
353 continue;
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;
367 bool error = false;
368 _devices.clear();
370 QFile controller("/dev/usb0");
371 int i = 1;
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;
381 di.udi_addr = addr;
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);
391 controller.close();
392 #ifndef Q_OS_NETBSD
393 } else {
394 error = true;
395 #endif
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."));
405 return true;
407 #endif