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.
34 #ifndef __DEBUG_MODE__
35 #define __DEBUG_MODE__
41 ///////////////////////////////////////////////////////////////////////////////
42 // Usb::Error exception class
44 static std::string
GetErrorString(int libusb_errcode
, const std::string
&str
)
46 std::ostringstream oss
;
49 if( libusb_errcode
) {
50 oss
<< std::setbase(10) << libusb_errcode
<< ", ";
53 // oss << strerror(-libusb_errno) << "): "
54 oss
<< usb_strerror() << "): ";
59 Error::Error(const std::string
&str
)
60 : Barry::Error(GetErrorString(0, str
))
65 Error::Error(int libusb_errcode
, const std::string
&str
)
66 : Barry::Error(GetErrorString(libusb_errcode
, str
))
67 , m_libusb_errcode(libusb_errcode
)
72 ///////////////////////////////////////////////////////////////////////////////
75 Match::Match(int vendor
, int product
,
76 const char *busname
, const char *devname
)
86 m_busses
= usb_get_busses();
93 bool Match::ToNum(const char *str
, long &num
)
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
)
114 if( ToNum(n1
, l1
) && ToNum(n2
, l2
) ) {
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
) )
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
) )
140 if( m_dev
->descriptor
.idVendor
== m_vendor
&&
141 m_dev
->descriptor
.idProduct
== m_product
) {
145 // advance for next time
148 m_busses
= m_busses
->next
;
159 ///////////////////////////////////////////////////////////////////////////////
162 Device::Device(Usb::DeviceIDType id
, int timeout
)
166 dout("usb_open(" << std::dec
<< id
<< ")");
168 throw Error("invalid USB device ID");
169 m_handle
= usb_open(id
);
171 throw Error("open failed");
176 dout("usb_close(" << std::dec
<< 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
);
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
);
198 dout("usb_reset(" << std::dec
<< m_handle
<< ")");
199 int ret
= usb_reset(m_handle
);
204 bool Device::BulkRead(int ep
, Barry::Data
&data
, int timeout
)
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
) {
214 if( ret
== -ETIMEDOUT
)
215 throw Timeout(ret
, "Timeout in usb_bulk_read");
217 throw Error(ret
, "Error in usb_bulk_read");
220 data
.ReleaseBuffer(ret
);
221 } while( ret
== -EINTR
|| ret
== -EAGAIN
);
226 bool Device::BulkWrite(int ep
, const Barry::Data
&data
, int timeout
)
228 ddout("BulkWrite to endpoint 0x" << std::hex
<< ep
<< ":\n" << data
);
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
) {
236 if( ret
== -ETIMEDOUT
)
237 throw Timeout(ret
, "Timeout in usb_bulk_write (1)");
239 throw Error(ret
, "Error in usb_bulk_write (1)");
241 } while( ret
== -EINTR
|| ret
== -EAGAIN
);
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
);
255 ret
= usb_bulk_write(m_handle
, ep
,
257 timeout
== -1 ? m_timeout
: timeout
);
258 if( ret
< 0 && ret
!= -EINTR
&& ret
!= -EAGAIN
) {
260 if( ret
== -ETIMEDOUT
)
261 throw Timeout(ret
, "Timeout in usb_bulk_write (2)");
263 throw Error(ret
, "Error in usb_bulk_write (2)");
265 } while( ret
== -EINTR
|| ret
== -EAGAIN
);
270 bool Device::InterruptRead(int ep
, Barry::Data
&data
, int timeout
)
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
) {
280 if( ret
== -ETIMEDOUT
)
281 throw Timeout(ret
, "Timeout in usb_interrupt_read");
283 throw Error(ret
, "Error in usb_interrupt_read");
286 data
.ReleaseBuffer(ret
);
287 } while( ret
== -EINTR
|| ret
== -EAGAIN
);
292 bool Device::InterruptWrite(int ep
, const Barry::Data
&data
, int timeout
)
294 ddout("InterruptWrite to endpoint 0x" << std::hex
<< ep
<< ":\n" << data
);
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
) {
303 if( ret
== -ETIMEDOUT
)
304 throw Timeout(ret
, "Timeout in usb_interrupt_write");
306 throw Error(ret
, "Error in usb_interrupt_write");
308 } while( ret
== -EINTR
|| ret
== -EAGAIN
);
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
)
323 while( BulkRead(ep
, data
, timeout
) )
326 catch( Usb::Error
& ) {}
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
;
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
;
362 ///////////////////////////////////////////////////////////////////////////////
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
);
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 ///////////////////////////////////////////////////////////////////////////////
385 bool EndpointDiscovery::Discover(struct usb_interface_descriptor
*interface
, int epcount
)
393 if( !interface
|| !interface
->endpoint
) {
394 dout("EndpointDiscovery::Discover: empty interface pointer");
398 for( int i
= 0; i
< epcount
; i
++ ) {
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
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...
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
) {
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
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
444 // save the type last
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
);
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
484 for( int i
= 0; i
< interface
->num_altsetting
; i
++ ) {
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
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.");
508 (*this)[desc
.desc
.bInterfaceNumber
] = desc
;
509 dout(" interface added to map with bInterfaceNumber: " << std::dec
<< (unsigned int)desc
.desc
.bInterfaceNumber
);
514 bool InterfaceDiscovery::Discover(Usb::DeviceIDType devid
, int cfgidx
, int ifcount
)
520 if( !devid
|| !devid
->config
|| !devid
->config
[cfgidx
].interface
) {
521 dout("InterfaceDiscovery::Discover: empty devid/config/interface");
525 for( int i
= 0; i
< ifcount
; i
++ ) {
526 if( !DiscoverInterface(&devid
->config
[cfgidx
].interface
[i
]) )
530 return m_valid
= true;
534 ///////////////////////////////////////////////////////////////////////////////
537 bool ConfigDiscovery::Discover(Usb::DeviceIDType devid
, int cfgcount
)
543 for( int i
= 0; i
< cfgcount
; i
++ ) {
546 if( !devid
|| !devid
->config
) {
547 dout("ConfigDiscovery::Discover: empty devid or config");
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
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
);
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.");
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 ///////////////////////////////////////////////////////////////////////////////
589 DeviceDiscovery::DeviceDiscovery(Usb::DeviceIDType devid
)
595 bool DeviceDiscovery::Discover(Usb::DeviceIDType devid
)
601 // copy the descriptor over to our memory
603 dout("DeviceDiscovery::Discover: empty devid");
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
626 m_valid
= configs
.Discover(devid
, desc
.bNumConfigurations
);