lib: add prefixes to enums to avoid name clashes on Windows (like ERROR)
[barry.git] / src / usbwrap.cc
blob50433639d3c72f5beb5563f2b1014f06335be823
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>
33 #ifndef __DEBUG_MODE__
34 #define __DEBUG_MODE__
35 #endif
36 #include "debug.h"
38 namespace Usb {
40 ///////////////////////////////////////////////////////////////////////////////
41 // Usb::Error exception class
43 static std::string GetErrorString(int libusb_errcode, const std::string &str)
45 std::ostringstream oss;
46 oss << "(";
48 if( libusb_errcode ) {
49 oss << std::setbase(10) << libusb_errcode << ", ";
52 // oss << strerror(-libusb_errno) << "): "
53 oss << usb_strerror() << "): ";
54 oss << str;
55 return oss.str();
58 Error::Error(const std::string &str)
59 : Barry::Error(GetErrorString(0, str))
60 , m_libusb_errcode(0)
64 Error::Error(int libusb_errcode, const std::string &str)
65 : Barry::Error(GetErrorString(libusb_errcode, str))
66 , m_libusb_errcode(libusb_errcode)
71 ///////////////////////////////////////////////////////////////////////////////
72 // Match
74 Match::Match(int vendor, int product,
75 const char *busname, const char *devname)
76 : m_busses(0)
77 , m_dev(0)
78 , m_vendor(vendor)
79 , m_product(product)
80 , m_busname(busname)
81 , m_devname(devname)
83 usb_find_busses();
84 usb_find_devices();
85 m_busses = usb_get_busses();
88 Match::~Match()
92 bool Match::ToNum(const char *str, long &num)
94 char *end = 0;
95 num = strtol(str, &end, 10);
96 return num >= 0 && // no negative numbers
97 num != LONG_MIN && num != LONG_MAX && // no overflow
98 str != end && *end == '\0'; // whole string valid
102 // Linux treats bus and device path names as numbers, sometimes left
103 // padded with zeros. Other platforms, such as Windows, use strings,
104 // such as "bus-1" or similar.
106 // Here we try to convert each string to a number, and if successful,
107 // compare them. If unable to convert, then compare as strings.
108 // This way, "3" == "003" and "bus-foobar" == "bus-foobar".
110 bool Match::NameCompare(const char *n1, const char *n2)
112 long l1, l2;
113 if( ToNum(n1, l1) && ToNum(n2, l2) ) {
114 return l1 == l2;
116 else {
117 return strcmp(n1, n2) == 0;
121 bool Match::next_device(Usb::DeviceIDType *devid)
123 for( ; m_busses; m_busses = m_busses->next ) {
125 // only search on given bus
126 if( m_busname && !NameCompare(m_busname, m_busses->dirname) )
127 continue;
129 if( !m_dev )
130 m_dev = m_busses->devices;
132 for( ; m_dev; m_dev = m_dev->next ) {
134 // search for specific device
135 if( m_devname && !NameCompare(m_devname, m_dev->filename) )
136 continue;
138 // is there a match?
139 if( m_dev->descriptor.idVendor == m_vendor &&
140 m_dev->descriptor.idProduct == m_product ) {
141 // found!
142 *devid = m_dev;
144 // advance for next time
145 m_dev = m_dev->next;
146 if( !m_dev )
147 m_busses = m_busses->next;
149 // done
150 return true;
154 return false;
158 ///////////////////////////////////////////////////////////////////////////////
159 // Device
161 Device::Device(Usb::DeviceIDType id, int timeout)
162 : m_id(id),
163 m_timeout(timeout)
165 dout("usb_open(" << std::dec << id << ")");
166 m_handle = usb_open(id);
167 if( !m_handle )
168 throw Error("open failed");
171 Device::~Device()
173 dout("usb_close(" << std::dec << m_handle << ")");
174 usb_close(m_handle);
177 bool Device::SetConfiguration(unsigned char cfg)
179 dout("usb_set_configuration(" << std::dec << m_handle << ", 0x" << std::hex << (unsigned int) cfg << ")");
180 int ret = usb_set_configuration(m_handle, cfg);
181 m_lasterror = ret;
182 return ret >= 0;
185 bool Device::ClearHalt(int ep)
187 dout("usb_clear_halt(" << std::dec << m_handle << ", 0x" << std::hex << ep << ")");
188 int ret = usb_clear_halt(m_handle, ep);
189 m_lasterror = ret;
190 return ret >= 0;
193 bool Device::Reset()
195 dout("usb_reset(" << std::dec << m_handle << ")");
196 int ret = usb_reset(m_handle);
197 m_lasterror = ret;
198 return ret == 0;
201 bool Device::BulkRead(int ep, Barry::Data &data, int timeout)
203 int ret;
204 do {
205 data.QuickZap();
206 ret = usb_bulk_read(m_handle, ep,
207 (char*) data.GetBuffer(), data.GetBufSize(),
208 timeout == -1 ? m_timeout : timeout);
209 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
210 m_lasterror = ret;
211 if( ret == -ETIMEDOUT )
212 throw Timeout(ret, "Timeout in usb_bulk_read");
213 else
214 throw Error(ret, "Error in usb_bulk_read");
216 else if( ret > 0 )
217 data.ReleaseBuffer(ret);
218 } while( ret == -EINTR || ret == -EAGAIN );
220 return ret >= 0;
223 bool Device::BulkWrite(int ep, const Barry::Data &data, int timeout)
225 ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
226 int ret;
227 do {
228 ret = usb_bulk_write(m_handle, ep,
229 (char*) data.GetData(), data.GetSize(),
230 timeout == -1 ? m_timeout : timeout);
231 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
232 m_lasterror = ret;
233 if( ret == -ETIMEDOUT )
234 throw Timeout(ret, "Timeout in usb_bulk_write (1)");
235 else
236 throw Error(ret, "Error in usb_bulk_write (1)");
238 } while( ret == -EINTR || ret == -EAGAIN );
240 return ret >= 0;
243 bool Device::BulkWrite(int ep, const void *data, size_t size, int timeout)
245 #ifdef __DEBUG_MODE__
246 Barry::Data dump(data, size);
247 ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << dump);
248 #endif
250 int ret;
251 do {
252 ret = usb_bulk_write(m_handle, ep,
253 (char*) data, size,
254 timeout == -1 ? m_timeout : timeout);
255 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
256 m_lasterror = ret;
257 if( ret == -ETIMEDOUT )
258 throw Timeout(ret, "Timeout in usb_bulk_write (2)");
259 else
260 throw Error(ret, "Error in usb_bulk_write (2)");
262 } while( ret == -EINTR || ret == -EAGAIN );
264 return ret >= 0;
267 bool Device::InterruptRead(int ep, Barry::Data &data, int timeout)
269 int ret;
270 do {
271 data.QuickZap();
272 ret = usb_interrupt_read(m_handle, ep,
273 (char*) data.GetBuffer(), data.GetBufSize(),
274 timeout == -1 ? m_timeout : timeout);
275 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
276 m_lasterror = ret;
277 if( ret == -ETIMEDOUT )
278 throw Timeout(ret, "Timeout in usb_interrupt_read");
279 else
280 throw Error(ret, "Error in usb_interrupt_read");
282 else if( ret > 0 )
283 data.ReleaseBuffer(ret);
284 } while( ret == -EINTR || ret == -EAGAIN );
286 return ret >= 0;
289 bool Device::InterruptWrite(int ep, const Barry::Data &data, int timeout)
291 ddout("InterruptWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
293 int ret;
294 do {
295 ret = usb_interrupt_write(m_handle, ep,
296 (char*) data.GetData(), data.GetSize(),
297 timeout == -1 ? m_timeout : timeout);
298 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
299 m_lasterror = ret;
300 if( ret == -ETIMEDOUT )
301 throw Timeout(ret, "Timeout in usb_interrupt_write");
302 else
303 throw Error(ret, "Error in usb_interrupt_write");
305 } while( ret == -EINTR || ret == -EAGAIN );
307 return ret >= 0;
311 // BulkDrain
313 /// Reads anything available on the given endpoint, with a low timeout,
314 /// in order to clear any pending reads.
316 void Device::BulkDrain(int ep, int timeout)
318 try {
319 Barry::Data data;
320 while( BulkRead(ep, data, timeout) )
323 catch( Usb::Error & ) {}
327 // GetConfiguration
329 /// Uses the GET_CONFIGURATION control message to determine the currently
330 /// selected USB configuration, returning it in the cfg argument.
331 /// If unsuccessful, returns false.
333 bool Device::GetConfiguration(unsigned char &cfg)
335 int result = usb_control_msg(m_handle, 0x80, USB_REQ_GET_CONFIGURATION, 0, 0,
336 (char*) &cfg, 1, m_timeout);
337 m_lasterror = result;
338 return result >= 0;
342 // SetAltInterface
344 /// Uses the usb_set_altinterface() function to set the currently
345 /// selected USB alternate setting of the current interface.
346 /// The iface parameter passed in should be a value specified
347 /// in the bAlternateSetting descriptor field.
348 /// If unsuccessful, returns false.
350 bool Device::SetAltInterface(int iface)
352 int result = usb_set_altinterface(m_handle, iface);
353 m_lasterror = result;
354 return result >= 0;
359 ///////////////////////////////////////////////////////////////////////////////
360 // Interface
362 Interface::Interface(Device &dev, int iface)
363 : m_dev(dev), m_iface(iface)
365 dout("usb_claim_interface(" << dev.GetHandle() << ", 0x" << std::hex << iface << ")");
366 int ret = usb_claim_interface(dev.GetHandle(), iface);
367 if( ret < 0 )
368 throw Error(ret, "claim interface failed");
371 Interface::~Interface()
373 dout("usb_release_interface(" << m_dev.GetHandle() << ", 0x" << std::hex << m_iface << ")");
374 usb_release_interface(m_dev.GetHandle(), m_iface);
379 ///////////////////////////////////////////////////////////////////////////////
380 // EndpointDiscovery
382 bool EndpointDiscovery::Discover(struct usb_interface_descriptor *interface, int epcount)
384 // start fresh
385 clear();
386 m_valid = false;
388 EndpointPair pair;
390 if( !interface || !interface->endpoint ) {
391 dout("EndpointDiscovery::Discover: empty interface pointer");
392 return false;
395 for( int i = 0; i < epcount; i++ ) {
396 // load descriptor
397 usb_endpoint_descriptor desc;
398 desc = interface->endpoint[i];
399 dout(" endpoint_desc #" << std::dec << i << " loaded"
400 << "\nbLength: " << std::dec << (unsigned ) desc.bLength
401 << "\nbDescriptorType: " << std::dec << (unsigned ) desc.bDescriptorType
402 << "\nbEndpointAddress: 0x" << std::hex << (unsigned ) desc.bEndpointAddress
403 << "\nbmAttributes: 0x" << std::hex << (unsigned ) desc.bmAttributes
404 << "\nwMaxPacketSize: " << std::dec << (unsigned ) desc.wMaxPacketSize
405 << "\nbInterval: " << std::dec << (unsigned ) desc.bInterval
406 << "\nbRefresh: " << std::dec << (unsigned ) desc.bRefresh
407 << "\nbSynchAddress: " << std::dec << (unsigned ) desc.bSynchAddress
408 << "\n"
411 // add to the map
412 (*this)[desc.bEndpointAddress] = desc;
413 dout(" endpoint added to map with bEndpointAddress: 0x" << std::hex << (unsigned int)desc.bEndpointAddress);
415 // parse the endpoint into read/write sets, if possible,
416 // going in discovery order...
417 // Assumptions:
418 // - endpoints of related utility will be grouped
419 // - endpoints with same type will be grouped
420 // - endpoints that do not meet the above assumptions
421 // do not belong in a pair
422 unsigned char type = desc.bmAttributes & USB_ENDPOINT_TYPE_MASK;
423 if( desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK ) {
424 // read endpoint
425 pair.read = desc.bEndpointAddress;
426 dout(" pair.read = 0x" << std::hex << (unsigned int)pair.read);
427 if( pair.IsTypeSet() && pair.type != type ) {
428 // if type is already set, we must start over
429 pair.write = 0;
432 else {
433 // write endpoint
434 pair.write = desc.bEndpointAddress;
435 dout(" pair.write = 0x" << std::hex << (unsigned int)pair.write);
436 if( pair.IsTypeSet() && pair.type != type ) {
437 // if type is already set, we must start over
438 pair.read = 0;
441 // save the type last
442 pair.type = type;
443 dout(" pair.type = 0x" << std::hex << (unsigned int)pair.type);
445 // if pair is complete, add to array
446 if( pair.IsComplete() ) {
447 m_endpoints.push_back(pair);
448 dout(" pair added! ("
449 << "read: 0x" << std::hex << (unsigned int)pair.read << ","
450 << "write: 0x" << std::hex << (unsigned int)pair.write << ","
451 << "type: 0x" << std::hex << (unsigned int)pair.type << ")");
452 pair = EndpointPair(); // clear
456 // just for debugging purposes, check for extra descriptors, and
457 // dump them to dout if they exist
458 if( interface->extra ) {
459 dout("while parsing endpoints, found a block of extra descriptors:");
460 Barry::Data data(interface->extra, interface->extralen);
461 dout(data);
464 return m_valid = true;
468 ///////////////////////////////////////////////////////////////////////////////
469 // InterfaceDiscovery
471 bool InterfaceDiscovery::DiscoverInterface(struct usb_interface *interface)
473 if( !interface->altsetting ) {
474 dout("InterfaceDiscovery::DiscoverIterface: empty altsetting");
475 // some devices are buggy and return a higher bNumInterfaces
476 // than the number of interfaces available... in this case
477 // we just skip and continue
478 return true;
481 for( int i = 0; i < interface->num_altsetting; i++ ) {
482 // load descriptor
483 InterfaceDesc desc;
484 desc.desc = interface->altsetting[i];
485 dout(" interface_desc #" << std::dec << i << " loaded"
486 << "\nbLength: " << std::dec << (unsigned) desc.desc.bLength
487 << "\nbDescriptorType: " << std::dec << (unsigned) desc.desc.bDescriptorType
488 << "\nbInterfaceNumber: " << std::dec << (unsigned) desc.desc.bInterfaceNumber
489 << "\nbAlternateSetting: " << std::dec << (unsigned) desc.desc.bAlternateSetting
490 << "\nbNumEndpoints: " << std::dec << (unsigned) desc.desc.bNumEndpoints
491 << "\nbInterfaceClass: " << std::dec << (unsigned) desc.desc.bInterfaceClass
492 << "\nbInterfaceSubClass: " << std::dec << (unsigned) desc.desc.bInterfaceSubClass
493 << "\nbInterfaceProtocol: " << std::dec << (unsigned) desc.desc.bInterfaceProtocol
494 << "\niInterface: " << std::dec << (unsigned) desc.desc.iInterface
495 << "\n"
498 // load all endpoints on this interface
499 if( !desc.endpoints.Discover(&desc.desc, desc.desc.bNumEndpoints) ) {
500 dout(" endpoint discovery failed for bInterfaceNumber: " << std::dec << (unsigned int)desc.desc.bInterfaceNumber << ", not added to map.");
501 return false;
504 // add to the map
505 (*this)[desc.desc.bInterfaceNumber] = desc;
506 dout(" interface added to map with bInterfaceNumber: " << std::dec << (unsigned int)desc.desc.bInterfaceNumber);
508 return true;
511 bool InterfaceDiscovery::Discover(Usb::DeviceIDType devid, int cfgidx, int ifcount)
513 // start fresh
514 clear();
515 m_valid = false;
517 if( !devid || !devid->config || !devid->config[cfgidx].interface ) {
518 dout("InterfaceDiscovery::Discover: empty devid/config/interface");
519 return false;
522 for( int i = 0; i < ifcount; i++ ) {
523 if( !DiscoverInterface(&devid->config[cfgidx].interface[i]) )
524 return false;
527 return m_valid = true;
531 ///////////////////////////////////////////////////////////////////////////////
532 // ConfigDiscovery
534 bool ConfigDiscovery::Discover(Usb::DeviceIDType devid, int cfgcount)
536 // start fresh
537 clear();
538 m_valid = false;
540 for( int i = 0; i < cfgcount; i++ ) {
541 // load descriptor
542 ConfigDesc desc;
543 if( !devid || !devid->config ) {
544 dout("ConfigDiscovery::Discover: empty devid or config");
545 return false;
547 desc.desc = devid->config[i];
548 dout(" config_desc #" << std::dec << i << " loaded"
549 << "\nbLength: " << std::dec << (unsigned int) desc.desc.bLength
550 << "\nbDescriptorType: " << std::dec << (unsigned int) desc.desc.bDescriptorType
551 << "\nwTotalLength: " << std::dec << (unsigned int) desc.desc.wTotalLength
552 << "\nbNumInterfaces: " << std::dec << (unsigned int) desc.desc.bNumInterfaces
553 << "\nbConfigurationValue: " << std::dec << (unsigned int) desc.desc.bConfigurationValue
554 << "\niConfiguration: " << std::dec << (unsigned int) desc.desc.iConfiguration
555 << "\nbmAttributes: 0x" << std::hex << (unsigned int) desc.desc.bmAttributes
556 << "\nMaxPower: " << std::dec << (unsigned int) desc.desc.MaxPower
557 << "\n"
560 // just for debugging purposes, check for extra descriptors, and
561 // dump them to dout if they exist
562 if( desc.desc.extra ) {
563 dout("while parsing config descriptor, found a block of extra descriptors:");
564 Barry::Data data(desc.desc.extra, desc.desc.extralen);
565 dout(data);
568 // load all interfaces on this configuration
569 if( !desc.interfaces.Discover(devid, i, desc.desc.bNumInterfaces) ) {
570 dout(" config discovery failed for bConfigurationValue: " << std::dec << (unsigned int)desc.desc.bConfigurationValue << ", not added to map.");
571 return false;
574 // add to the map
575 (*this)[desc.desc.bConfigurationValue] = desc;
576 dout(" config added to map with bConfigurationValue: " << std::dec << (unsigned int)desc.desc.bConfigurationValue);
579 return m_valid = true;
583 ///////////////////////////////////////////////////////////////////////////////
584 // DeviceDiscovery
586 DeviceDiscovery::DeviceDiscovery(Usb::DeviceIDType devid)
587 : m_valid(false)
589 Discover(devid);
592 bool DeviceDiscovery::Discover(Usb::DeviceIDType devid)
594 // start fresh
595 configs.clear();
596 m_valid = false;
598 // copy the descriptor over to our memory
599 if( !devid ) {
600 dout("DeviceDiscovery::Discover: empty devid");
601 return false;
604 desc = devid->descriptor;
605 dout("device_desc loaded"
606 << "\nbLength: " << std::dec << (unsigned int) desc.bLength
607 << "\nbDescriptorType: " << std::dec << (unsigned int) desc.bDescriptorType
608 << "\nbcdUSB: 0x" << std::hex << (unsigned int) desc.bcdUSB
609 << "\nbDeviceClass: " << std::dec << (unsigned int) desc.bDeviceClass
610 << "\nbDeviceSubClass: " << std::dec << (unsigned int) desc.bDeviceSubClass
611 << "\nbDeviceProtocol: " << std::dec << (unsigned int) desc.bDeviceProtocol
612 << "\nbMaxPacketSize0: " << std::dec << (unsigned int) desc.bMaxPacketSize0
613 << "\nidVendor: 0x" << std::hex << (unsigned int) desc.idVendor
614 << "\nidProduct: 0x" << std::hex << (unsigned int) desc.idProduct
615 << "\nbcdDevice: 0x" << std::hex << (unsigned int) desc.bcdDevice
616 << "\niManufacturer: " << std::dec << (unsigned int) desc.iManufacturer
617 << "\niProduct: " << std::dec << (unsigned int) desc.iProduct
618 << "\niSerialNumber: " << std::dec << (unsigned int) desc.iSerialNumber
619 << "\nbNumConfigurations: " << std::dec << (unsigned int) desc.bNumConfigurations
620 << "\n"
623 m_valid = configs.Discover(devid, desc.bNumConfigurations);
624 return m_valid;
627 } // namespace Usb