lib: show offset and rectype in HexDumpParser
[barry.git] / src / usbwrap.cc
blob10045d8e8bc8341fbf5854f1d039380471804155
1 ///
2 /// \file usbwrap.cc
3 /// USB API wrapper
4 ///
6 /*
7 Copyright (C) 2005-2010, Chris Frey
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
23 #include "usbwrap.h"
24 #include "data.h"
25 #include "error.h"
26 #include "debug.h"
28 #include <iomanip>
29 #include <sstream>
30 #include <errno.h>
31 #include <string.h>
32 #include <limits.h>
34 #ifndef __DEBUG_MODE__
35 #define __DEBUG_MODE__
36 #endif
37 #include "debug.h"
39 namespace Usb {
41 ///////////////////////////////////////////////////////////////////////////////
42 // Usb::Error exception class
44 static std::string GetErrorString(int libusb_errcode, const std::string &str)
46 std::ostringstream oss;
47 oss << "(";
49 if( libusb_errcode ) {
50 oss << std::setbase(10) << libusb_errcode << ", ";
53 // oss << strerror(-libusb_errno) << "): "
54 oss << usb_strerror() << "): ";
55 oss << str;
56 return oss.str();
59 Error::Error(const std::string &str)
60 : Barry::Error(GetErrorString(0, str))
61 , m_libusb_errcode(0)
65 Error::Error(int libusb_errcode, const std::string &str)
66 : Barry::Error(GetErrorString(libusb_errcode, str))
67 , m_libusb_errcode(libusb_errcode)
72 ///////////////////////////////////////////////////////////////////////////////
73 // Match
75 Match::Match(int vendor, int product,
76 const char *busname, const char *devname)
77 : m_busses(0)
78 , m_dev(0)
79 , m_vendor(vendor)
80 , m_product(product)
81 , m_busname(busname)
82 , m_devname(devname)
84 usb_find_busses();
85 usb_find_devices();
86 m_busses = usb_get_busses();
89 Match::~Match()
93 bool Match::ToNum(const char *str, long &num)
95 char *end = 0;
96 num = strtol(str, &end, 10);
97 return num >= 0 && // no negative numbers
98 num != LONG_MIN && num != LONG_MAX && // no overflow
99 str != end && *end == '\0'; // whole string valid
103 // Linux treats bus and device path names as numbers, sometimes left
104 // padded with zeros. Other platforms, such as Windows, use strings,
105 // such as "bus-1" or similar.
107 // Here we try to convert each string to a number, and if successful,
108 // compare them. If unable to convert, then compare as strings.
109 // This way, "3" == "003" and "bus-foobar" == "bus-foobar".
111 bool Match::NameCompare(const char *n1, const char *n2)
113 long l1, l2;
114 if( ToNum(n1, l1) && ToNum(n2, l2) ) {
115 return l1 == l2;
117 else {
118 return strcmp(n1, n2) == 0;
122 bool Match::next_device(Usb::DeviceIDType *devid)
124 for( ; m_busses; m_busses = m_busses->next ) {
126 // only search on given bus
127 if( m_busname && !NameCompare(m_busname, m_busses->dirname) )
128 continue;
130 if( !m_dev )
131 m_dev = m_busses->devices;
133 for( ; m_dev; m_dev = m_dev->next ) {
135 // search for specific device
136 if( m_devname && !NameCompare(m_devname, m_dev->filename) )
137 continue;
139 // is there a match?
140 if( m_dev->descriptor.idVendor == m_vendor &&
141 m_dev->descriptor.idProduct == m_product ) {
142 // found!
143 *devid = m_dev;
145 // advance for next time
146 m_dev = m_dev->next;
147 if( !m_dev )
148 m_busses = m_busses->next;
150 // done
151 return true;
155 return false;
159 ///////////////////////////////////////////////////////////////////////////////
160 // Device
162 Device::Device(Usb::DeviceIDType id, int timeout)
163 : m_id(id),
164 m_timeout(timeout)
166 dout("usb_open(" << std::dec << id << ")");
167 if( !id )
168 throw Error("invalid USB device ID");
169 m_handle = usb_open(id);
170 if( !m_handle )
171 throw Error("open failed");
174 Device::~Device()
176 dout("usb_close(" << std::dec << m_handle << ")");
177 usb_close(m_handle);
180 bool Device::SetConfiguration(unsigned char cfg)
182 dout("usb_set_configuration(" << std::dec << m_handle << ", 0x" << std::hex << (unsigned int) cfg << ")");
183 int ret = usb_set_configuration(m_handle, cfg);
184 m_lasterror = ret;
185 return ret >= 0;
188 bool Device::ClearHalt(int ep)
190 dout("usb_clear_halt(" << std::dec << m_handle << ", 0x" << std::hex << ep << ")");
191 int ret = usb_clear_halt(m_handle, ep);
192 m_lasterror = ret;
193 return ret >= 0;
196 bool Device::Reset()
198 dout("usb_reset(" << std::dec << m_handle << ")");
199 int ret = usb_reset(m_handle);
200 m_lasterror = ret;
201 return ret == 0;
204 bool Device::BulkRead(int ep, Barry::Data &data, int timeout)
206 int ret;
207 do {
208 data.QuickZap();
209 ret = usb_bulk_read(m_handle, ep,
210 (char*) data.GetBuffer(), data.GetBufSize(),
211 timeout == -1 ? m_timeout : timeout);
212 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
213 m_lasterror = ret;
214 if( ret == -ETIMEDOUT )
215 throw Timeout(ret, "Timeout in usb_bulk_read");
216 else
217 throw Error(ret, "Error in usb_bulk_read");
219 else if( ret > 0 )
220 data.ReleaseBuffer(ret);
221 } while( ret == -EINTR || ret == -EAGAIN );
223 return ret >= 0;
226 bool Device::BulkWrite(int ep, const Barry::Data &data, int timeout)
228 ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
229 int ret;
230 do {
231 ret = usb_bulk_write(m_handle, ep,
232 (char*) data.GetData(), data.GetSize(),
233 timeout == -1 ? m_timeout : timeout);
234 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
235 m_lasterror = ret;
236 if( ret == -ETIMEDOUT )
237 throw Timeout(ret, "Timeout in usb_bulk_write (1)");
238 else
239 throw Error(ret, "Error in usb_bulk_write (1)");
241 } while( ret == -EINTR || ret == -EAGAIN );
243 return ret >= 0;
246 bool Device::BulkWrite(int ep, const void *data, size_t size, int timeout)
248 #ifdef __DEBUG_MODE__
249 Barry::Data dump(data, size);
250 ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << dump);
251 #endif
253 int ret;
254 do {
255 ret = usb_bulk_write(m_handle, ep,
256 (char*) data, size,
257 timeout == -1 ? m_timeout : timeout);
258 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
259 m_lasterror = ret;
260 if( ret == -ETIMEDOUT )
261 throw Timeout(ret, "Timeout in usb_bulk_write (2)");
262 else
263 throw Error(ret, "Error in usb_bulk_write (2)");
265 } while( ret == -EINTR || ret == -EAGAIN );
267 return ret >= 0;
270 bool Device::InterruptRead(int ep, Barry::Data &data, int timeout)
272 int ret;
273 do {
274 data.QuickZap();
275 ret = usb_interrupt_read(m_handle, ep,
276 (char*) data.GetBuffer(), data.GetBufSize(),
277 timeout == -1 ? m_timeout : timeout);
278 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
279 m_lasterror = ret;
280 if( ret == -ETIMEDOUT )
281 throw Timeout(ret, "Timeout in usb_interrupt_read");
282 else
283 throw Error(ret, "Error in usb_interrupt_read");
285 else if( ret > 0 )
286 data.ReleaseBuffer(ret);
287 } while( ret == -EINTR || ret == -EAGAIN );
289 return ret >= 0;
292 bool Device::InterruptWrite(int ep, const Barry::Data &data, int timeout)
294 ddout("InterruptWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
296 int ret;
297 do {
298 ret = usb_interrupt_write(m_handle, ep,
299 (char*) data.GetData(), data.GetSize(),
300 timeout == -1 ? m_timeout : timeout);
301 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
302 m_lasterror = ret;
303 if( ret == -ETIMEDOUT )
304 throw Timeout(ret, "Timeout in usb_interrupt_write");
305 else
306 throw Error(ret, "Error in usb_interrupt_write");
308 } while( ret == -EINTR || ret == -EAGAIN );
310 return ret >= 0;
314 // BulkDrain
316 /// Reads anything available on the given endpoint, with a low timeout,
317 /// in order to clear any pending reads.
319 void Device::BulkDrain(int ep, int timeout)
321 try {
322 Barry::Data data;
323 while( BulkRead(ep, data, timeout) )
326 catch( Usb::Error & ) {}
330 // GetConfiguration
332 /// Uses the GET_CONFIGURATION control message to determine the currently
333 /// selected USB configuration, returning it in the cfg argument.
334 /// If unsuccessful, returns false.
336 bool Device::GetConfiguration(unsigned char &cfg)
338 int result = usb_control_msg(m_handle, 0x80, USB_REQ_GET_CONFIGURATION, 0, 0,
339 (char*) &cfg, 1, m_timeout);
340 m_lasterror = result;
341 return result >= 0;
345 // SetAltInterface
347 /// Uses the usb_set_altinterface() function to set the currently
348 /// selected USB alternate setting of the current interface.
349 /// The iface parameter passed in should be a value specified
350 /// in the bAlternateSetting descriptor field.
351 /// If unsuccessful, returns false.
353 bool Device::SetAltInterface(int iface)
355 int result = usb_set_altinterface(m_handle, iface);
356 m_lasterror = result;
357 return result >= 0;
362 ///////////////////////////////////////////////////////////////////////////////
363 // Interface
365 Interface::Interface(Device &dev, int iface)
366 : m_dev(dev), m_iface(iface)
368 dout("usb_claim_interface(" << dev.GetHandle() << ", 0x" << std::hex << iface << ")");
369 int ret = usb_claim_interface(dev.GetHandle(), iface);
370 if( ret < 0 )
371 throw Error(ret, "claim interface failed");
374 Interface::~Interface()
376 dout("usb_release_interface(" << m_dev.GetHandle() << ", 0x" << std::hex << m_iface << ")");
377 usb_release_interface(m_dev.GetHandle(), m_iface);
382 ///////////////////////////////////////////////////////////////////////////////
383 // EndpointDiscovery
385 bool EndpointDiscovery::Discover(struct usb_interface_descriptor *interface, int epcount)
387 // start fresh
388 clear();
389 m_valid = false;
391 EndpointPair pair;
393 if( !interface || !interface->endpoint ) {
394 dout("EndpointDiscovery::Discover: empty interface pointer");
395 return false;
398 for( int i = 0; i < epcount; i++ ) {
399 // load descriptor
400 usb_endpoint_descriptor desc;
401 desc = interface->endpoint[i];
402 dout(" endpoint_desc #" << std::dec << i << " loaded"
403 << "\nbLength: " << std::dec << (unsigned ) desc.bLength
404 << "\nbDescriptorType: " << std::dec << (unsigned ) desc.bDescriptorType
405 << "\nbEndpointAddress: 0x" << std::hex << (unsigned ) desc.bEndpointAddress
406 << "\nbmAttributes: 0x" << std::hex << (unsigned ) desc.bmAttributes
407 << "\nwMaxPacketSize: " << std::dec << (unsigned ) desc.wMaxPacketSize
408 << "\nbInterval: " << std::dec << (unsigned ) desc.bInterval
409 << "\nbRefresh: " << std::dec << (unsigned ) desc.bRefresh
410 << "\nbSynchAddress: " << std::dec << (unsigned ) desc.bSynchAddress
411 << "\n"
414 // add to the map
415 (*this)[desc.bEndpointAddress] = desc;
416 dout(" endpoint added to map with bEndpointAddress: 0x" << std::hex << (unsigned int)desc.bEndpointAddress);
418 // parse the endpoint into read/write sets, if possible,
419 // going in discovery order...
420 // Assumptions:
421 // - endpoints of related utility will be grouped
422 // - endpoints with same type will be grouped
423 // - endpoints that do not meet the above assumptions
424 // do not belong in a pair
425 unsigned char type = desc.bmAttributes & USB_ENDPOINT_TYPE_MASK;
426 if( desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK ) {
427 // read endpoint
428 pair.read = desc.bEndpointAddress;
429 dout(" pair.read = 0x" << std::hex << (unsigned int)pair.read);
430 if( pair.IsTypeSet() && pair.type != type ) {
431 // if type is already set, we must start over
432 pair.write = 0;
435 else {
436 // write endpoint
437 pair.write = desc.bEndpointAddress;
438 dout(" pair.write = 0x" << std::hex << (unsigned int)pair.write);
439 if( pair.IsTypeSet() && pair.type != type ) {
440 // if type is already set, we must start over
441 pair.read = 0;
444 // save the type last
445 pair.type = type;
446 dout(" pair.type = 0x" << std::hex << (unsigned int)pair.type);
448 // if pair is complete, add to array
449 if( pair.IsComplete() ) {
450 m_endpoints.push_back(pair);
451 dout(" pair added! ("
452 << "read: 0x" << std::hex << (unsigned int)pair.read << ","
453 << "write: 0x" << std::hex << (unsigned int)pair.write << ","
454 << "type: 0x" << std::hex << (unsigned int)pair.type << ")");
455 pair = EndpointPair(); // clear
459 // just for debugging purposes, check for extra descriptors, and
460 // dump them to dout if they exist
461 if( interface->extra ) {
462 dout("while parsing endpoints, found a block of extra descriptors:");
463 Barry::Data data(interface->extra, interface->extralen);
464 dout(data);
467 return m_valid = true;
471 ///////////////////////////////////////////////////////////////////////////////
472 // InterfaceDiscovery
474 bool InterfaceDiscovery::DiscoverInterface(struct usb_interface *interface)
476 if( !interface->altsetting ) {
477 dout("InterfaceDiscovery::DiscoverIterface: empty altsetting");
478 // some devices are buggy and return a higher bNumInterfaces
479 // than the number of interfaces available... in this case
480 // we just skip and continue
481 return true;
484 for( int i = 0; i < interface->num_altsetting; i++ ) {
485 // load descriptor
486 InterfaceDesc desc;
487 desc.desc = interface->altsetting[i];
488 dout(" interface_desc #" << std::dec << i << " loaded"
489 << "\nbLength: " << std::dec << (unsigned) desc.desc.bLength
490 << "\nbDescriptorType: " << std::dec << (unsigned) desc.desc.bDescriptorType
491 << "\nbInterfaceNumber: " << std::dec << (unsigned) desc.desc.bInterfaceNumber
492 << "\nbAlternateSetting: " << std::dec << (unsigned) desc.desc.bAlternateSetting
493 << "\nbNumEndpoints: " << std::dec << (unsigned) desc.desc.bNumEndpoints
494 << "\nbInterfaceClass: " << std::dec << (unsigned) desc.desc.bInterfaceClass
495 << "\nbInterfaceSubClass: " << std::dec << (unsigned) desc.desc.bInterfaceSubClass
496 << "\nbInterfaceProtocol: " << std::dec << (unsigned) desc.desc.bInterfaceProtocol
497 << "\niInterface: " << std::dec << (unsigned) desc.desc.iInterface
498 << "\n"
501 // load all endpoints on this interface
502 if( !desc.endpoints.Discover(&desc.desc, desc.desc.bNumEndpoints) ) {
503 dout(" endpoint discovery failed for bInterfaceNumber: " << std::dec << (unsigned int)desc.desc.bInterfaceNumber << ", not added to map.");
504 return false;
507 // add to the map
508 (*this)[desc.desc.bInterfaceNumber] = desc;
509 dout(" interface added to map with bInterfaceNumber: " << std::dec << (unsigned int)desc.desc.bInterfaceNumber);
511 return true;
514 bool InterfaceDiscovery::Discover(Usb::DeviceIDType devid, int cfgidx, int ifcount)
516 // start fresh
517 clear();
518 m_valid = false;
520 if( !devid || !devid->config || !devid->config[cfgidx].interface ) {
521 dout("InterfaceDiscovery::Discover: empty devid/config/interface");
522 return false;
525 for( int i = 0; i < ifcount; i++ ) {
526 if( !DiscoverInterface(&devid->config[cfgidx].interface[i]) )
527 return false;
530 return m_valid = true;
534 ///////////////////////////////////////////////////////////////////////////////
535 // ConfigDiscovery
537 bool ConfigDiscovery::Discover(Usb::DeviceIDType devid, int cfgcount)
539 // start fresh
540 clear();
541 m_valid = false;
543 for( int i = 0; i < cfgcount; i++ ) {
544 // load descriptor
545 ConfigDesc desc;
546 if( !devid || !devid->config ) {
547 dout("ConfigDiscovery::Discover: empty devid or config");
548 return false;
550 desc.desc = devid->config[i];
551 dout(" config_desc #" << std::dec << i << " loaded"
552 << "\nbLength: " << std::dec << (unsigned int) desc.desc.bLength
553 << "\nbDescriptorType: " << std::dec << (unsigned int) desc.desc.bDescriptorType
554 << "\nwTotalLength: " << std::dec << (unsigned int) desc.desc.wTotalLength
555 << "\nbNumInterfaces: " << std::dec << (unsigned int) desc.desc.bNumInterfaces
556 << "\nbConfigurationValue: " << std::dec << (unsigned int) desc.desc.bConfigurationValue
557 << "\niConfiguration: " << std::dec << (unsigned int) desc.desc.iConfiguration
558 << "\nbmAttributes: 0x" << std::hex << (unsigned int) desc.desc.bmAttributes
559 << "\nMaxPower: " << std::dec << (unsigned int) desc.desc.MaxPower
560 << "\n"
563 // just for debugging purposes, check for extra descriptors, and
564 // dump them to dout if they exist
565 if( desc.desc.extra ) {
566 dout("while parsing config descriptor, found a block of extra descriptors:");
567 Barry::Data data(desc.desc.extra, desc.desc.extralen);
568 dout(data);
571 // load all interfaces on this configuration
572 if( !desc.interfaces.Discover(devid, i, desc.desc.bNumInterfaces) ) {
573 dout(" config discovery failed for bConfigurationValue: " << std::dec << (unsigned int)desc.desc.bConfigurationValue << ", not added to map.");
574 return false;
577 // add to the map
578 (*this)[desc.desc.bConfigurationValue] = desc;
579 dout(" config added to map with bConfigurationValue: " << std::dec << (unsigned int)desc.desc.bConfigurationValue);
582 return m_valid = true;
586 ///////////////////////////////////////////////////////////////////////////////
587 // DeviceDiscovery
589 DeviceDiscovery::DeviceDiscovery(Usb::DeviceIDType devid)
590 : m_valid(false)
592 Discover(devid);
595 bool DeviceDiscovery::Discover(Usb::DeviceIDType devid)
597 // start fresh
598 configs.clear();
599 m_valid = false;
601 // copy the descriptor over to our memory
602 if( !devid ) {
603 dout("DeviceDiscovery::Discover: empty devid");
604 return false;
607 desc = devid->descriptor;
608 dout("device_desc loaded"
609 << "\nbLength: " << std::dec << (unsigned int) desc.bLength
610 << "\nbDescriptorType: " << std::dec << (unsigned int) desc.bDescriptorType
611 << "\nbcdUSB: 0x" << std::hex << (unsigned int) desc.bcdUSB
612 << "\nbDeviceClass: " << std::dec << (unsigned int) desc.bDeviceClass
613 << "\nbDeviceSubClass: " << std::dec << (unsigned int) desc.bDeviceSubClass
614 << "\nbDeviceProtocol: " << std::dec << (unsigned int) desc.bDeviceProtocol
615 << "\nbMaxPacketSize0: " << std::dec << (unsigned int) desc.bMaxPacketSize0
616 << "\nidVendor: 0x" << std::hex << (unsigned int) desc.idVendor
617 << "\nidProduct: 0x" << std::hex << (unsigned int) desc.idProduct
618 << "\nbcdDevice: 0x" << std::hex << (unsigned int) desc.bcdDevice
619 << "\niManufacturer: " << std::dec << (unsigned int) desc.iManufacturer
620 << "\niProduct: " << std::dec << (unsigned int) desc.iProduct
621 << "\niSerialNumber: " << std::dec << (unsigned int) desc.iSerialNumber
622 << "\nbNumConfigurations: " << std::dec << (unsigned int) desc.bNumConfigurations
623 << "\n"
626 m_valid = configs.Discover(devid, desc.bNumConfigurations);
627 return m_valid;
630 } // namespace Usb