Release tarball for barry-0.9
[barry.git] / src / usbwrap.cc
blob93f4f4e53a3275a5571a4c6b57c5995c20f8c196
1 ///
2 /// \file usbwrap.cc
3 /// USB API wrapper
4 ///
6 /*
7 Copyright (C) 2005-2007, 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>
32 #ifndef __DEBUG_MODE__
33 #define __DEBUG_MODE__
34 #endif
35 #include "debug.h"
37 namespace Usb {
39 ///////////////////////////////////////////////////////////////////////////////
40 // Usb::Error exception class
42 static std::string GetErrorString(int libusb_errcode, const std::string &str)
44 std::ostringstream oss;
45 oss << "(";
47 if( libusb_errcode ) {
48 oss << std::setbase(10) << libusb_errcode << ", ";
51 // oss << strerror(-libusb_errno) << "): "
52 oss << usb_strerror() << "): ";
53 oss << str;
54 return oss.str();
57 Error::Error(const std::string &str)
58 : Barry::Error(GetErrorString(0, str))
59 , m_libusb_errcode(0)
63 Error::Error(int libusb_errcode, const std::string &str)
64 : Barry::Error(GetErrorString(libusb_errcode, str))
65 , m_libusb_errcode(libusb_errcode)
70 ///////////////////////////////////////////////////////////////////////////////
71 // Match
73 Match::Match(int vendor, int product)
74 : m_busses(0),
75 m_dev(0),
76 m_vendor(vendor),
77 m_product(product)
79 usb_find_busses();
80 usb_find_devices();
81 m_busses = usb_get_busses();
84 Match::~Match()
88 bool Match::next_device(Usb::DeviceIDType *devid)
90 for( ; m_busses; m_busses = m_busses->next ) {
92 if( !m_dev )
93 m_dev = m_busses->devices;
95 for( ; m_dev; m_dev = m_dev->next ) {
96 // is there a match?
97 if( m_dev->descriptor.idVendor == m_vendor &&
98 m_dev->descriptor.idProduct == m_product ) {
99 // found!
100 *devid = m_dev;
102 // advance for next time
103 m_dev = m_dev->next;
104 if( !m_dev )
105 m_busses = m_busses->next;
107 // done
108 return true;
112 return false;
116 ///////////////////////////////////////////////////////////////////////////////
117 // Device
119 Device::Device(Usb::DeviceIDType id, int timeout)
120 : m_id(id),
121 m_timeout(timeout)
123 dout("usb_open(" << std::dec << id << ")");
124 m_handle = usb_open(id);
125 if( !m_handle )
126 throw Error("open failed");
129 Device::~Device()
131 dout("usb_close(" << std::dec << m_handle << ")");
132 usb_close(m_handle);
135 bool Device::SetConfiguration(unsigned char cfg)
137 dout("usb_set_configuration(" << std::dec << m_handle << "," << std::dec << (unsigned int) cfg << ")");
138 int ret = usb_set_configuration(m_handle, cfg);
139 m_lasterror = ret;
140 return ret >= 0;
143 bool Device::ClearHalt(int ep)
145 dout("usb_clear_halt(" << std::dec << m_handle << "," << std::dec << ep << ")");
146 int ret = usb_clear_halt(m_handle, ep);
147 m_lasterror = ret;
148 return ret >= 0;
151 bool Device::Reset()
153 dout("usb_reset(" << std::dec << m_handle << ")");
154 int ret = usb_reset(m_handle);
155 m_lasterror = ret;
156 return ret == 0;
159 bool Device::BulkRead(int ep, Barry::Data &data, int timeout)
161 int ret;
162 do {
163 ret = usb_bulk_read(m_handle, ep,
164 (char*) data.GetBuffer(), data.GetBufSize(),
165 timeout == -1 ? m_timeout : timeout);
166 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
167 m_lasterror = ret;
168 if( ret == -ETIMEDOUT )
169 throw Timeout(ret, "Timeout in usb_bulk_read");
170 else
171 throw Error(ret, "Error in usb_bulk_read");
173 data.ReleaseBuffer(ret);
174 } while( ret == -EINTR || ret == -EAGAIN );
176 return ret >= 0;
179 bool Device::BulkWrite(int ep, const Barry::Data &data, int timeout)
181 ddout("BulkWrite to endpoint " << std::dec << ep << ":\n" << data);
182 int ret;
183 do {
184 ret = usb_bulk_write(m_handle, ep,
185 (char*) data.GetData(), data.GetSize(),
186 timeout == -1 ? m_timeout : timeout);
187 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
188 m_lasterror = ret;
189 if( ret == -ETIMEDOUT )
190 throw Timeout(ret, "Timeout in usb_bulk_read");
191 else
192 throw Error(ret, "Error in usb_bulk_read");
194 } while( ret == -EINTR || ret == -EAGAIN );
196 return ret >= 0;
199 bool Device::BulkWrite(int ep, const void *data, size_t size, int timeout)
201 #ifdef __DEBUG_MODE__
202 Barry::Data dump(data, size);
203 ddout("BulkWrite to endpoint " << std::dec << ep << ":\n" << dump);
204 #endif
206 int ret;
207 do {
208 ret = usb_bulk_write(m_handle, ep,
209 (char*) data, size,
210 timeout == -1 ? m_timeout : timeout);
211 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
212 m_lasterror = ret;
213 if( ret == -ETIMEDOUT )
214 throw Timeout(ret, "Timeout in usb_bulk_read");
215 else
216 throw Error(ret, "Error in usb_bulk_read");
218 } while( ret == -EINTR || ret == -EAGAIN );
220 return ret >= 0;
223 bool Device::InterruptRead(int ep, Barry::Data &data, int timeout)
225 int ret;
226 do {
227 ret = usb_interrupt_read(m_handle, ep,
228 (char*) data.GetBuffer(), data.GetBufSize(),
229 timeout == -1 ? m_timeout : timeout);
230 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
231 m_lasterror = ret;
232 if( ret == -ETIMEDOUT )
233 throw Timeout(ret, "Timeout in usb_bulk_read");
234 else
235 throw Error(ret, "Error in usb_bulk_read");
237 data.ReleaseBuffer(ret);
238 } while( ret == -EINTR || ret == -EAGAIN );
240 return ret >= 0;
243 bool Device::InterruptWrite(int ep, const Barry::Data &data, int timeout)
245 ddout("InterruptWrite to endpoint " << std::dec << ep << ":\n" << data);
247 int ret;
248 do {
249 ret = usb_interrupt_write(m_handle, ep,
250 (char*) data.GetData(), data.GetSize(),
251 timeout == -1 ? m_timeout : timeout);
252 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
253 m_lasterror = ret;
254 if( ret == -ETIMEDOUT )
255 throw Timeout(ret, "Timeout in usb_bulk_read");
256 else
257 throw Error(ret, "Error in usb_bulk_read");
259 } while( ret == -EINTR || ret == -EAGAIN );
261 return ret >= 0;
265 // BulkDrain
267 /// Reads anything available on the given endpoint, with a low timeout,
268 /// in order to clear any pending reads.
270 void Device::BulkDrain(int ep)
272 try {
273 Barry::Data data;
274 while( BulkRead(ep, data, 100) )
277 catch( Usb::Error & ) {}
282 ///////////////////////////////////////////////////////////////////////////////
283 // Interface
285 Interface::Interface(Device &dev, int iface)
286 : m_dev(dev), m_iface(iface)
288 dout("usb_claim_interface(" << dev.GetHandle() << "," << std::dec << iface << ")");
289 int ret = usb_claim_interface(dev.GetHandle(), iface);
290 if( ret < 0 )
291 throw Error(ret, "claim interface failed");
294 Interface::~Interface()
296 dout("usb_release_interface(" << m_dev.GetHandle() << "," << std::dec << m_iface << ")");
297 usb_release_interface(m_dev.GetHandle(), m_iface);
302 ///////////////////////////////////////////////////////////////////////////////
303 // EndpointDiscovery
305 bool EndpointDiscovery::Discover(struct usb_interface_descriptor *interface, int epcount)
307 // start fresh
308 clear();
309 m_valid = false;
311 EndpointPair pair;
313 if( !interface || !interface->endpoint ) {
314 dout("EndpointDiscovery::Discover: empty interface pointer");
315 return false;
318 for( int i = 0; i < epcount; i++ ) {
319 // load descriptor
320 usb_endpoint_descriptor desc;
321 desc = interface->endpoint[i];
322 dout(" endpoint_desc #" << i << " loaded"
323 << "\nbLength: " << (unsigned ) desc.bLength
324 << "\nbDescriptorType: " << (unsigned ) desc.bDescriptorType
325 << "\nbEndpointAddress: " << (unsigned ) desc.bEndpointAddress
326 << "\nbmAttributes: " << (unsigned ) desc.bmAttributes
327 << "\nwMaxPacketSize: " << (unsigned ) desc.wMaxPacketSize
328 << "\nbInterval: " << (unsigned ) desc.bInterval
329 << "\nbRefresh: " << (unsigned ) desc.bRefresh
330 << "\nbSynchAddress: " << (unsigned ) desc.bSynchAddress
331 << "\n"
334 // add to the map
335 (*this)[desc.bEndpointAddress] = desc;
336 dout(" endpoint added to map with bEndpointAddress: " << (unsigned int)desc.bEndpointAddress);
338 // parse the endpoint into read/write sets, if possible,
339 // going in discovery order...
340 // Assumptions:
341 // - endpoints of related utility will be grouped
342 // - endpoints with same type will be grouped
343 // - endpoints that do not meet the above assumptions
344 // do not belong in a pair
345 unsigned char type = desc.bmAttributes & USB_ENDPOINT_TYPE_MASK;
346 if( desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK ) {
347 // read endpoint
348 pair.read = desc.bEndpointAddress;
349 dout(" pair.read = " << (unsigned int)pair.read);
350 if( pair.IsTypeSet() && pair.type != type ) {
351 // if type is already set, we must start over
352 pair.write = 0;
355 else {
356 // write endpoint
357 pair.write = desc.bEndpointAddress;
358 dout(" pair.write = " << (unsigned int)pair.write);
359 if( pair.IsTypeSet() && pair.type != type ) {
360 // if type is already set, we must start over
361 pair.read = 0;
364 // save the type last
365 pair.type = type;
366 dout(" pair.type = " << (unsigned int)pair.type);
368 // if pair is complete, add to array
369 if( pair.IsComplete() ) {
370 m_endpoints.push_back(pair);
371 dout(" pair added! ("
372 << "read: " << (unsigned int)pair.read << ","
373 << "write: " << (unsigned int)pair.write << ","
374 << "type: " << (unsigned int)pair.type << ")");
375 pair = EndpointPair(); // clear
379 // just for debugging purposes, check for extra descriptors, and
380 // dump them to dout if they exist
381 if( interface->extra ) {
382 dout("while parsing endpoints, found a block of extra descriptors:");
383 Barry::Data data(interface->extra, interface->extralen);
384 dout(data);
387 return m_valid = true;
391 ///////////////////////////////////////////////////////////////////////////////
392 // InterfaceDiscovery
394 bool InterfaceDiscovery::DiscoverInterface(struct usb_interface *interface)
396 if( !interface->altsetting ) {
397 dout("InterfaceDiscovery::DiscoverIterface: empty altsetting");
398 // some devices are buggy and return a higher bNumInterfaces
399 // than the number of interfaces available... in this case
400 // we just skip and continue
401 return true;
404 for( int i = 0; i < interface->num_altsetting; i++ ) {
405 // load descriptor
406 InterfaceDesc desc;
407 desc.desc = interface->altsetting[i];
408 dout(" interface_desc #" << i << " loaded"
409 << "\nbLength: " << (unsigned) desc.desc.bLength
410 << "\nbDescriptorType: " << (unsigned) desc.desc.bDescriptorType
411 << "\nbInterfaceNumber: " << (unsigned) desc.desc.bInterfaceNumber
412 << "\nbAlternateSetting: " << (unsigned) desc.desc.bAlternateSetting
413 << "\nbNumEndpoints: " << (unsigned) desc.desc.bNumEndpoints
414 << "\nbInterfaceClass: " << (unsigned) desc.desc.bInterfaceClass
415 << "\nbInterfaceSubClass: " << (unsigned) desc.desc.bInterfaceSubClass
416 << "\nbInterfaceProtocol: " << (unsigned) desc.desc.bInterfaceProtocol
417 << "\niInterface: " << (unsigned) desc.desc.iInterface
418 << "\n"
421 // load all endpoints on this interface
422 if( !desc.endpoints.Discover(&desc.desc, desc.desc.bNumEndpoints) ) {
423 dout(" endpoint discovery failed for bInterfaceNumber: " << (unsigned int)desc.desc.bInterfaceNumber << ", not added to map.");
424 return false;
427 // add to the map
428 (*this)[desc.desc.bInterfaceNumber] = desc;
429 dout(" interface added to map with bInterfaceNumber: " << (unsigned int)desc.desc.bInterfaceNumber);
431 return true;
434 bool InterfaceDiscovery::Discover(Usb::DeviceIDType devid, int cfgidx, int ifcount)
436 // start fresh
437 clear();
438 m_valid = false;
440 if( !devid || !devid->config || !devid->config[cfgidx].interface ) {
441 dout("InterfaceDiscovery::Discover: empty devid/config/interface");
442 return false;
445 for( int i = 0; i < ifcount; i++ ) {
446 if( !DiscoverInterface(&devid->config[cfgidx].interface[i]) )
447 return false;
450 return m_valid = true;
454 ///////////////////////////////////////////////////////////////////////////////
455 // ConfigDiscovery
457 bool ConfigDiscovery::Discover(Usb::DeviceIDType devid, int cfgcount)
459 // start fresh
460 clear();
461 m_valid = false;
463 for( int i = 0; i < cfgcount; i++ ) {
464 // load descriptor
465 ConfigDesc desc;
466 if( !devid || !devid->config ) {
467 dout("ConfigDiscovery::Discover: empty devid or config");
468 return false;
470 desc.desc = devid->config[i];
471 dout(" config_desc #" << i << " loaded"
472 << "\nbLength: " << (unsigned int) desc.desc.bLength
473 << "\nbDescriptorType: " << (unsigned int) desc.desc.bDescriptorType
474 << "\nwTotalLength: " << (unsigned int) desc.desc.wTotalLength
475 << "\nbNumInterfaces: " << (unsigned int) desc.desc.bNumInterfaces
476 << "\nbConfigurationValue: " << (unsigned int) desc.desc.bConfigurationValue
477 << "\niConfiguration: " << (unsigned int) desc.desc.iConfiguration
478 << "\nbmAttributes: " << (unsigned int) desc.desc.bmAttributes
479 << "\nMaxPower: " << (unsigned int) desc.desc.MaxPower
480 << "\n"
483 // just for debugging purposes, check for extra descriptors, and
484 // dump them to dout if they exist
485 if( desc.desc.extra ) {
486 dout("while parsing config descriptor, found a block of extra descriptors:");
487 Barry::Data data(desc.desc.extra, desc.desc.extralen);
488 dout(data);
491 // load all interfaces on this configuration
492 if( !desc.interfaces.Discover(devid, i, desc.desc.bNumInterfaces) ) {
493 dout(" config discovery failed for bConfigurationValue: " << (unsigned int)desc.desc.bConfigurationValue << ", not added to map.");
494 return false;
497 // add to the map
498 (*this)[desc.desc.bConfigurationValue] = desc;
499 dout(" config added to map with bConfigurationValue: " << (unsigned int)desc.desc.bConfigurationValue);
502 return m_valid = true;
506 ///////////////////////////////////////////////////////////////////////////////
507 // DeviceDiscovery
509 DeviceDiscovery::DeviceDiscovery(Usb::DeviceIDType devid)
510 : m_valid(false)
512 Discover(devid);
515 bool DeviceDiscovery::Discover(Usb::DeviceIDType devid)
517 // start fresh
518 configs.clear();
519 m_valid = false;
521 // copy the descriptor over to our memory
522 if( !devid ) {
523 dout("DeviceDiscovery::Discover: empty devid");
524 return false;
527 desc = devid->descriptor;
528 dout("device_desc loaded"
529 << "\nbLength: " << (unsigned int) desc.bLength
530 << "\nbDescriptorType: " << (unsigned int) desc.bDescriptorType
531 << "\nbcdUSB: " << (unsigned int) desc.bcdUSB
532 << "\nbDeviceClass: " << (unsigned int) desc.bDeviceClass
533 << "\nbDeviceSubClass: " << (unsigned int) desc.bDeviceSubClass
534 << "\nbDeviceProtocol: " << (unsigned int) desc.bDeviceProtocol
535 << "\nbMaxPacketSize0: " << (unsigned int) desc.bMaxPacketSize0
536 << "\nidVendor: " << (unsigned int) desc.idVendor
537 << "\nidProduct: " << (unsigned int) desc.idProduct
538 << "\nbcdDevice: " << (unsigned int) desc.bcdDevice
539 << "\niManufacturer: " << (unsigned int) desc.iManufacturer
540 << "\niProduct: " << (unsigned int) desc.iProduct
541 << "\niSerialNumber: " << (unsigned int) desc.iSerialNumber
542 << "\nbNumConfigurations: " << (unsigned int) desc.bNumConfigurations
543 << "\n"
546 m_valid = configs.Discover(devid, desc.bNumConfigurations);
547 return m_valid;
550 } // namespace Usb