- opensync plugin:
[barry.git] / src / usbwrap.cc
blob3786f0962e3cd67d038984c74c6a72369a0aaa4f
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 // Match
42 Match::Match(int vendor, int product)
43 : m_busses(0),
44 m_dev(0),
45 m_vendor(vendor),
46 m_product(product)
48 usb_find_busses();
49 usb_find_devices();
50 m_busses = usb_get_busses();
53 Match::~Match()
57 bool Match::next_device(Usb::DeviceIDType *devid)
59 for( ; m_busses; m_busses = m_busses->next ) {
61 if( !m_dev )
62 m_dev = m_busses->devices;
64 for( ; m_dev; m_dev = m_dev->next ) {
65 // is there a match?
66 if( m_dev->descriptor.idVendor == m_vendor &&
67 m_dev->descriptor.idProduct == m_product ) {
68 // found!
69 *devid = m_dev;
71 // advance for next time
72 m_dev = m_dev->next;
73 if( !m_dev )
74 m_busses = m_busses->next;
76 // done
77 return true;
81 return false;
85 ///////////////////////////////////////////////////////////////////////////////
86 // Device
88 Device::Device(Usb::DeviceIDType id, int timeout)
89 : m_id(id),
90 m_timeout(timeout)
92 m_handle = usb_open(id);
93 if( !m_handle )
94 throw Error("open failed");
97 Device::~Device()
99 usb_close(m_handle);
102 bool Device::SetConfiguration(unsigned char cfg)
104 int ret = usb_set_configuration(m_handle, cfg);
105 m_lasterror = ret;
106 return ret >= 0;
109 bool Device::ClearHalt(int ep)
111 int ret = usb_clear_halt(m_handle, ep);
112 m_lasterror = ret;
113 return ret >= 0;
116 bool Device::Reset()
118 int ret = usb_reset(m_handle);
119 m_lasterror = ret;
120 return ret == 0;
123 bool Device::BulkRead(int ep, Barry::Data &data, int timeout)
125 int ret;
126 do {
127 ret = usb_bulk_read(m_handle, ep,
128 (char*) data.GetBuffer(), data.GetBufSize(),
129 timeout == -1 ? m_timeout : timeout);
130 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
131 m_lasterror = ret;
132 if( ret == -ETIMEDOUT )
133 throw Timeout("Timeout in usb_bulk_read");
134 else
135 throw Error("Error in usb_bulk_read");
137 data.ReleaseBuffer(ret);
138 } while( ret == -EINTR || ret == -EAGAIN );
140 return ret >= 0;
143 bool Device::BulkWrite(int ep, const Barry::Data &data, int timeout)
145 ddout("BulkWrite to endpoint " << ep << ":\n" << data);
146 int ret;
147 do {
148 ret = usb_bulk_write(m_handle, ep,
149 (char*) data.GetData(), data.GetSize(),
150 timeout == -1 ? m_timeout : timeout);
151 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
152 m_lasterror = ret;
153 if( ret == -ETIMEDOUT )
154 throw Timeout("Timeout in usb_bulk_read");
155 else
156 throw Error("Error in usb_bulk_read");
158 } while( ret == -EINTR || ret == -EAGAIN );
160 return ret >= 0;
163 bool Device::BulkWrite(int ep, const void *data, size_t size, int timeout)
165 #ifdef __DEBUG_MODE__
166 Barry::Data dump(data, size);
167 ddout("BulkWrite to endpoint " << ep << ":\n" << dump);
168 #endif
170 int ret;
171 do {
172 ret = usb_bulk_write(m_handle, ep,
173 (char*) data, size,
174 timeout == -1 ? m_timeout : timeout);
175 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
176 m_lasterror = ret;
177 if( ret == -ETIMEDOUT )
178 throw Timeout("Timeout in usb_bulk_read");
179 else
180 throw Error("Error in usb_bulk_read");
182 } while( ret == -EINTR || ret == -EAGAIN );
184 return ret >= 0;
187 bool Device::InterruptRead(int ep, Barry::Data &data, int timeout)
189 int ret;
190 do {
191 ret = usb_interrupt_read(m_handle, ep,
192 (char*) data.GetBuffer(), data.GetBufSize(),
193 timeout == -1 ? m_timeout : timeout);
194 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
195 m_lasterror = ret;
196 if( ret == -ETIMEDOUT )
197 throw Timeout("Timeout in usb_bulk_read");
198 else
199 throw Error("Error in usb_bulk_read");
201 data.ReleaseBuffer(ret);
202 } while( ret == -EINTR || ret == -EAGAIN );
204 return ret >= 0;
207 bool Device::InterruptWrite(int ep, const Barry::Data &data, int timeout)
209 ddout("InterruptWrite to endpoint " << ep << ":\n" << data);
211 int ret;
212 do {
213 ret = usb_interrupt_write(m_handle, ep,
214 (char*) data.GetData(), data.GetSize(),
215 timeout == -1 ? m_timeout : timeout);
216 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
217 m_lasterror = ret;
218 if( ret == -ETIMEDOUT )
219 throw Timeout("Timeout in usb_bulk_read");
220 else
221 throw Error("Error in usb_bulk_read");
223 } while( ret == -EINTR || ret == -EAGAIN );
225 return ret >= 0;
229 // BulkDrain
231 /// Reads anything available on the given endpoint, with a low timeout,
232 /// in order to clear any pending reads.
234 void Device::BulkDrain(int ep)
236 try {
237 Barry::Data data;
238 while( BulkRead(ep, data, 100) )
241 catch( Usb::Error & ) {}
246 ///////////////////////////////////////////////////////////////////////////////
247 // EndpointDiscovery
249 bool EndpointDiscovery::Discover(struct usb_interface_descriptor *interface, int epcount)
251 // start fresh
252 clear();
253 m_valid = false;
255 EndpointPair pair;
257 if( !interface || !interface->endpoint ) {
258 dout("EndpointDiscovery::Discover: empty interface pointer");
259 return false;
262 for( int i = 0; i < epcount; i++ ) {
263 // load descriptor
264 usb_endpoint_descriptor desc;
265 desc = interface->endpoint[i];
266 dout(" endpoint_desc #" << i << " loaded"
267 << "\nbLength: " << (unsigned ) desc.bLength
268 << "\nbDescriptorType: " << (unsigned ) desc.bDescriptorType
269 << "\nbEndpointAddress: " << (unsigned ) desc.bEndpointAddress
270 << "\nbmAttributes: " << (unsigned ) desc.bmAttributes
271 << "\nwMaxPacketSize: " << (unsigned ) desc.wMaxPacketSize
272 << "\nbInterval: " << (unsigned ) desc.bInterval
273 << "\nbRefresh: " << (unsigned ) desc.bRefresh
274 << "\nbSynchAddress: " << (unsigned ) desc.bSynchAddress
275 << "\n"
278 // add to the map
279 (*this)[desc.bEndpointAddress] = desc;
280 dout(" endpoint added to map with bEndpointAddress: " << (unsigned int)desc.bEndpointAddress);
282 // parse the endpoint into read/write sets, if possible,
283 // going in discovery order...
284 // Assumptions:
285 // - endpoints of related utility will be grouped
286 // - endpoints with same type will be grouped
287 // - endpoints that do not meet the above assumptions
288 // do not belong in a pair
289 unsigned char type = desc.bmAttributes & USB_ENDPOINT_TYPE_MASK;
290 if( desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK ) {
291 // read endpoint
292 pair.read = desc.bEndpointAddress;
293 dout(" pair.read = " << (unsigned int)pair.read);
294 if( pair.IsTypeSet() && pair.type != type ) {
295 // if type is already set, we must start over
296 pair.write = 0;
299 else {
300 // write endpoint
301 pair.write = desc.bEndpointAddress;
302 dout(" pair.write = " << (unsigned int)pair.write);
303 if( pair.IsTypeSet() && pair.type != type ) {
304 // if type is already set, we must start over
305 pair.read = 0;
308 // save the type last
309 pair.type = type;
310 dout(" pair.type = " << (unsigned int)pair.type);
312 // if pair is complete, add to array
313 if( pair.IsComplete() ) {
314 m_endpoints.push_back(pair);
315 dout(" pair added! ("
316 << "read: " << (unsigned int)pair.read << ","
317 << "write: " << (unsigned int)pair.write << ","
318 << "type: " << (unsigned int)pair.type << ")");
319 pair = EndpointPair(); // clear
323 // just for debugging purposes, check for extra descriptors, and
324 // dump them to dout if they exist
325 if( interface->extra ) {
326 dout("while parsing endpoints, found a block of extra descriptors:");
327 Barry::Data data(interface->extra, interface->extralen);
328 dout(data);
331 return m_valid = true;
335 ///////////////////////////////////////////////////////////////////////////////
336 // InterfaceDiscovery
338 bool InterfaceDiscovery::DiscoverInterface(struct usb_interface *interface)
340 if( !interface->altsetting ) {
341 dout("InterfaceDiscovery::DiscoverIterface: empty altsetting");
342 // some devices are buggy and return a higher bNumInterfaces
343 // than the number of interfaces available... in this case
344 // we just skip and continue
345 return true;
348 for( int i = 0; i < interface->num_altsetting; i++ ) {
349 // load descriptor
350 InterfaceDesc desc;
351 desc.desc = interface->altsetting[i];
352 dout(" interface_desc #" << i << " loaded"
353 << "\nbLength: " << (unsigned) desc.desc.bLength
354 << "\nbDescriptorType: " << (unsigned) desc.desc.bDescriptorType
355 << "\nbInterfaceNumber: " << (unsigned) desc.desc.bInterfaceNumber
356 << "\nbAlternateSetting: " << (unsigned) desc.desc.bAlternateSetting
357 << "\nbNumEndpoints: " << (unsigned) desc.desc.bNumEndpoints
358 << "\nbInterfaceClass: " << (unsigned) desc.desc.bInterfaceClass
359 << "\nbInterfaceSubClass: " << (unsigned) desc.desc.bInterfaceSubClass
360 << "\nbInterfaceProtocol: " << (unsigned) desc.desc.bInterfaceProtocol
361 << "\niInterface: " << (unsigned) desc.desc.iInterface
362 << "\n"
365 // load all endpoints on this interface
366 if( !desc.endpoints.Discover(&desc.desc, desc.desc.bNumEndpoints) ) {
367 dout(" endpoint discovery failed for bInterfaceNumber: " << (unsigned int)desc.desc.bInterfaceNumber << ", not added to map.");
368 return false;
371 // add to the map
372 (*this)[desc.desc.bInterfaceNumber] = desc;
373 dout(" interface added to map with bInterfaceNumber: " << (unsigned int)desc.desc.bInterfaceNumber);
375 return true;
378 bool InterfaceDiscovery::Discover(Usb::DeviceIDType devid, int cfgidx, int ifcount)
380 // start fresh
381 clear();
382 m_valid = false;
384 if( !devid || !devid->config || !devid->config[cfgidx].interface ) {
385 dout("InterfaceDiscovery::Discover: empty devid/config/interface");
386 return false;
389 for( int i = 0; i < ifcount; i++ ) {
390 if( !DiscoverInterface(&devid->config[cfgidx].interface[i]) )
391 return false;
394 return m_valid = true;
398 ///////////////////////////////////////////////////////////////////////////////
399 // ConfigDiscovery
401 bool ConfigDiscovery::Discover(Usb::DeviceIDType devid, int cfgcount)
403 // start fresh
404 clear();
405 m_valid = false;
407 for( int i = 0; i < cfgcount; i++ ) {
408 // load descriptor
409 ConfigDesc desc;
410 if( !devid || !devid->config ) {
411 dout("ConfigDiscovery::Discover: empty devid or config");
412 return false;
414 desc.desc = devid->config[i];
415 dout(" config_desc #" << i << " loaded"
416 << "\nbLength: " << (unsigned int) desc.desc.bLength
417 << "\nbDescriptorType: " << (unsigned int) desc.desc.bDescriptorType
418 << "\nwTotalLength: " << (unsigned int) desc.desc.wTotalLength
419 << "\nbNumInterfaces: " << (unsigned int) desc.desc.bNumInterfaces
420 << "\nbConfigurationValue: " << (unsigned int) desc.desc.bConfigurationValue
421 << "\niConfiguration: " << (unsigned int) desc.desc.iConfiguration
422 << "\nbmAttributes: " << (unsigned int) desc.desc.bmAttributes
423 << "\nMaxPower: " << (unsigned int) desc.desc.MaxPower
424 << "\n"
427 // just for debugging purposes, check for extra descriptors, and
428 // dump them to dout if they exist
429 if( desc.desc.extra ) {
430 dout("while parsing config descriptor, found a block of extra descriptors:");
431 Barry::Data data(desc.desc.extra, desc.desc.extralen);
432 dout(data);
435 // load all interfaces on this configuration
436 if( !desc.interfaces.Discover(devid, i, desc.desc.bNumInterfaces) ) {
437 dout(" config discovery failed for bConfigurationValue: " << (unsigned int)desc.desc.bConfigurationValue << ", not added to map.");
438 return false;
441 // add to the map
442 (*this)[desc.desc.bConfigurationValue] = desc;
443 dout(" config added to map with bConfigurationValue: " << (unsigned int)desc.desc.bConfigurationValue);
446 return m_valid = true;
450 ///////////////////////////////////////////////////////////////////////////////
451 // DeviceDiscovery
453 DeviceDiscovery::DeviceDiscovery(Usb::DeviceIDType devid)
454 : m_valid(false)
456 Discover(devid);
459 bool DeviceDiscovery::Discover(Usb::DeviceIDType devid)
461 // start fresh
462 configs.clear();
463 m_valid = false;
465 // copy the descriptor over to our memory
466 if( !devid ) {
467 dout("DeviceDiscovery::Discover: empty devid");
468 return false;
471 desc = devid->descriptor;
472 dout("device_desc loaded"
473 << "\nbLength: " << (unsigned int) desc.bLength
474 << "\nbDescriptorType: " << (unsigned int) desc.bDescriptorType
475 << "\nbcdUSB: " << (unsigned int) desc.bcdUSB
476 << "\nbDeviceClass: " << (unsigned int) desc.bDeviceClass
477 << "\nbDeviceSubClass: " << (unsigned int) desc.bDeviceSubClass
478 << "\nbDeviceProtocol: " << (unsigned int) desc.bDeviceProtocol
479 << "\nbMaxPacketSize0: " << (unsigned int) desc.bMaxPacketSize0
480 << "\nidVendor: " << (unsigned int) desc.idVendor
481 << "\nidProduct: " << (unsigned int) desc.idProduct
482 << "\nbcdDevice: " << (unsigned int) desc.bcdDevice
483 << "\niManufacturer: " << (unsigned int) desc.iManufacturer
484 << "\niProduct: " << (unsigned int) desc.iProduct
485 << "\niSerialNumber: " << (unsigned int) desc.iSerialNumber
486 << "\nbNumConfigurations: " << (unsigned int) desc.bNumConfigurations
487 << "\n"
490 m_valid = configs.Discover(devid, desc.bNumConfigurations);
491 return m_valid;
494 } // namespace Usb