maint: added make-yum.sh for creating yum repositories under build/
[barry/progweb.git] / src / usbwrap_libusb.cc
blobf8d470a3bb4581a0f0dbdf2b5ef7ede9ef1be8fa
1 ///
2 /// \file usbwrap_libusb.cc
3 /// USB API wrapper for libusb version 0.1
4 ///
6 /*
7 Copyright (C) 2005-2011, Chris Frey
8 Portions Copyright (C) 2011, RealVNC Ltd.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "usbwrap_libusb.h"
25 #include "debug.h"
26 #include "data.h"
27 #include <errno.h>
28 #include <string.h>
29 #include <iostream>
30 #include <sstream>
31 #include <algorithm>
33 #ifndef __DEBUG_MODE__
34 #define __DEBUG_MODE__
35 #endif
36 #include "debug.h"
38 namespace Usb {
40 // helper function to make deleting pointers in maps and vectors easier
41 template<typename T> static void deletePtr(T* ptr) {
42 delete ptr;
45 template<typename K, typename T> static void deleteMapPtr(std::pair<K,T*> ptr) {
46 delete ptr.second;
49 ///////////////////////////////////////////////////////////////////////////////
50 // Static functions
52 std::string LibraryInterface::GetLastErrorString(int /*libusb_errcode*/)
54 // Errcode is unused by libusb, so just call the last error
55 return std::string(usb_strerror());
58 int LibraryInterface::TranslateErrcode(int libusb_errcode)
60 // libusb errcode == system errcode
61 return libusb_errcode;
64 int LibraryInterface::Init()
66 // if the environment variable USB_DEBUG is set, that
67 // level value will be used instead of our 9 below...
68 // if you need to *force* this to 9, call SetDataDump(true)
69 // after Init()
70 usb_init();
71 // Can never fail, so return success
72 return 0;
75 void LibraryInterface::Uninit()
77 // Nothing to do
80 void LibraryInterface::SetDataDump(bool data_dump_mode)
82 if( data_dump_mode )
83 usb_set_debug(9);
84 else
85 usb_set_debug(0);
88 ///////////////////////////////////////////////////////////////////////////////
89 // DeviceID
91 DeviceID::DeviceID(DeviceIDImpl* impl)
92 : m_impl(impl)
96 DeviceID::~DeviceID()
100 const char* DeviceID::GetBusName() const
102 return m_impl->m_dev->bus->dirname;
105 uint16_t DeviceID::GetNumber() const
107 return m_impl->m_dev->devnum;
110 const char* DeviceID::GetFilename() const
112 return m_impl->m_dev->filename;
115 uint16_t DeviceID::GetIdProduct() const
117 return m_impl->m_dev->descriptor.idProduct;
120 ///////////////////////////////////////////////////////////////////////////////
121 // DeviceList
123 DeviceList::DeviceList()
124 : m_impl(new DeviceListImpl())
126 // Work out what devices are on the bus at the moment
127 usb_find_busses();
128 usb_find_devices();
129 struct usb_bus* busses = usb_get_busses();
130 for( ; busses; busses = busses->next ) {
131 struct usb_device* dev = busses->devices;
132 for( ; dev; dev = dev->next ) {
133 // Add the device to the list of devices
134 std::auto_ptr<DeviceIDImpl> impl( new DeviceIDImpl() );
135 impl->m_dev = dev;
136 DeviceID devID(impl.release());
137 m_impl->m_devices.push_back(devID);
142 DeviceList::~DeviceList()
147 static bool ToNum(const char *str, long &num)
149 char *end = 0;
150 num = strtol(str, &end, 10);
151 return num >= 0 && // no negative numbers
152 num != LONG_MIN && num != LONG_MAX && // no overflow
153 str != end && *end == '\0'; // whole string valid
157 // Linux treats bus and device path names as numbers, sometimes left
158 // padded with zeros. Other platforms, such as Windows, use strings,
159 // such as "bus-1" or similar.
161 // Here we try to convert each string to a number, and if successful,
162 // compare them. If unable to convert, then compare as strings.
163 // This way, "3" == "003" and "bus-foobar" == "bus-foobar".
165 static bool NameCompare(const char *n1, const char *n2)
167 long l1, l2;
168 if( ToNum(n1, l1) && ToNum(n2, l2) ) {
169 return l1 == l2;
171 else {
172 return strcmp(n1, n2) == 0;
176 std::vector<DeviceID> DeviceList::MatchDevices(int vendor, int product,
177 const char *busname, const char *devname)
179 std::vector<DeviceID> ret;
181 std::vector<DeviceID>::iterator iter = m_impl->m_devices.begin();
183 for( ; iter != m_impl->m_devices.end() ; ++iter ) {
184 struct usb_device* dev = iter->m_impl->m_dev;
186 // only search on given bus
187 if( busname && !NameCompare(busname, dev->bus->dirname) )
188 continue;
190 // search for specific device
191 if( devname && !NameCompare(devname, dev->filename) )
192 continue;
194 // is there a match?
195 if( dev->descriptor.idVendor == vendor &&
196 ( dev->descriptor.idProduct == product ||
197 product == PRODUCT_ANY )) {
198 ret.push_back(*iter);
202 return ret;
205 ///////////////////////////////////////////////////////////////////////////////
206 // Device
208 Device::Device(const Usb::DeviceID& id, int timeout)
209 : m_id(id),
210 m_timeout(timeout)
212 dout("usb_open(" << std::dec << id.m_impl.get() << ")");
213 if( !id.m_impl.get() )
214 throw Error("invalid USB device ID");
215 m_handle.reset(new DeviceHandle());
216 m_handle->m_handle = usb_open(id.m_impl->m_dev);
217 if( !m_handle->m_handle )
218 throw Error("open failed");
221 Device::~Device()
223 dout("usb_close(" << std::dec << m_handle->m_handle << ")");
224 usb_close(m_handle->m_handle);
227 bool Device::SetConfiguration(unsigned char cfg)
229 dout("usb_set_configuration(" << std::dec << m_handle->m_handle << ", 0x" << std::hex << (unsigned int) cfg << ")");
230 int ret = usb_set_configuration(m_handle->m_handle, cfg);
231 m_lasterror = ret;
232 return ret >= 0;
235 bool Device::ClearHalt(int ep)
237 dout("usb_clear_halt(" << std::dec << m_handle->m_handle << ", 0x" << std::hex << ep << ")");
238 int ret = usb_clear_halt(m_handle->m_handle, ep);
239 m_lasterror = ret;
240 return ret >= 0;
243 bool Device::Reset()
245 dout("usb_reset(" << std::dec << m_handle->m_handle << ")");
246 int ret = usb_reset(m_handle->m_handle);
247 m_lasterror = ret;
248 return ret == 0;
251 bool Device::BulkRead(int ep, Barry::Data &data, int timeout)
253 int ret;
254 do {
255 data.QuickZap();
256 ret = usb_bulk_read(m_handle->m_handle, ep,
257 (char*) data.GetBuffer(), data.GetBufSize(),
258 timeout == -1 ? m_timeout : timeout);
259 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
260 m_lasterror = ret;
261 if( ret == -ETIMEDOUT )
262 throw Timeout(ret, "Timeout in usb_bulk_read");
263 else {
264 std::ostringstream oss;
265 oss << "Error in usb_bulk_read("
266 << m_handle->m_handle << ", "
267 << ep << ", buf, "
268 << data.GetBufSize() << ")";
269 throw Error(ret, oss.str());
272 else if( ret > 0 )
273 data.ReleaseBuffer(ret);
274 } while( ret == -EINTR || ret == -EAGAIN );
276 return ret >= 0;
279 bool Device::BulkWrite(int ep, const Barry::Data &data, int timeout)
281 ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
282 int ret;
283 do {
284 ret = usb_bulk_write(m_handle->m_handle, ep,
285 (char*) data.GetData(), data.GetSize(),
286 timeout == -1 ? m_timeout : timeout);
287 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
288 m_lasterror = ret;
289 if( ret == -ETIMEDOUT )
290 throw Timeout(ret, "Timeout in usb_bulk_write (1)");
291 else
292 throw Error(ret, "Error in usb_bulk_write (1)");
294 } while( ret == -EINTR || ret == -EAGAIN );
296 return ret >= 0;
299 bool Device::BulkWrite(int ep, const void *data, size_t size, int timeout)
301 #ifdef __DEBUG_MODE__
302 Barry::Data dump(data, size);
303 ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << dump);
304 #endif
306 int ret;
307 do {
308 ret = usb_bulk_write(m_handle->m_handle, ep,
309 (char*) data, size,
310 timeout == -1 ? m_timeout : timeout);
311 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
312 m_lasterror = ret;
313 if( ret == -ETIMEDOUT )
314 throw Timeout(ret, "Timeout in usb_bulk_write (2)");
315 else
316 throw Error(ret, "Error in usb_bulk_write (2)");
318 } while( ret == -EINTR || ret == -EAGAIN );
320 return ret >= 0;
323 bool Device::InterruptRead(int ep, Barry::Data &data, int timeout)
325 int ret;
326 do {
327 data.QuickZap();
328 ret = usb_interrupt_read(m_handle->m_handle, ep,
329 (char*) data.GetBuffer(), data.GetBufSize(),
330 timeout == -1 ? m_timeout : timeout);
331 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
332 m_lasterror = ret;
333 if( ret == -ETIMEDOUT )
334 throw Timeout(ret, "Timeout in usb_interrupt_read");
335 else
336 throw Error(ret, "Error in usb_interrupt_read");
338 else if( ret > 0 )
339 data.ReleaseBuffer(ret);
340 } while( ret == -EINTR || ret == -EAGAIN );
342 return ret >= 0;
345 bool Device::InterruptWrite(int ep, const Barry::Data &data, int timeout)
347 ddout("InterruptWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
349 int ret;
350 do {
351 ret = usb_interrupt_write(m_handle->m_handle, ep,
352 (char*) data.GetData(), data.GetSize(),
353 timeout == -1 ? m_timeout : timeout);
354 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
355 m_lasterror = ret;
356 if( ret == -ETIMEDOUT )
357 throw Timeout(ret, "Timeout in usb_interrupt_write");
358 else
359 throw Error(ret, "Error in usb_interrupt_write");
361 } while( ret == -EINTR || ret == -EAGAIN );
363 return ret >= 0;
367 // BulkDrain
369 /// Reads anything available on the given endpoint, with a low timeout,
370 /// in order to clear any pending reads.
372 void Device::BulkDrain(int ep, int timeout)
374 try {
375 Barry::Data data;
376 while( BulkRead(ep, data, timeout) )
379 catch( Usb::Error & ) {}
383 // GetConfiguration
385 /// Uses the GET_CONFIGURATION control message to determine the currently
386 /// selected USB configuration, returning it in the cfg argument.
387 /// If unsuccessful, returns false.
389 bool Device::GetConfiguration(unsigned char &cfg)
391 int result = usb_control_msg(m_handle->m_handle, 0x80, USB_REQ_GET_CONFIGURATION, 0, 0,
392 (char*) &cfg, 1, m_timeout);
393 m_lasterror = result;
394 return result >= 0;
397 // Returns the current power level of the device, or 0 if unknown
398 int Device::GetPowerLevel()
400 if( !m_id.m_impl->m_dev->config ||
401 !m_id.m_impl->m_dev->descriptor.bNumConfigurations < 1 )
402 return 0;
404 return m_id.m_impl->m_dev->config[0].MaxPower;
407 // Requests that the kernel driver is detached, returning false on failure
408 bool Device::DetachKernelDriver(int iface)
410 #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
411 int result = usb_detach_kernel_driver_np(m_handle->m_handle, iface);
412 m_lasterror = result;
413 return result >= 0;
414 #else
415 m_lasterror = -ENOSYS;
416 return false;
417 #endif
420 // Sends a control message to the device, returning false on failure
421 bool Device::ControlMsg(int requesttype, int request, int value,
422 int index, char *bytes, int size, int timeout)
424 int result = usb_control_msg(m_handle->m_handle,
425 requesttype, request, value, index,
426 bytes, size, timeout);
427 m_lasterror = result;
428 return result >= 0;
432 int Device::FindInterface(int ifaceClass)
434 struct usb_config_descriptor *cfg = m_id.m_impl->m_dev->config;
436 if( cfg ) {
438 for( unsigned i = 0; cfg->interface && i < cfg->bNumInterfaces; i++ ) {
439 struct usb_interface *iface = &cfg->interface[i];
440 for( int a = 0; iface->altsetting && a < iface->num_altsetting; a++ ) {
441 struct usb_interface_descriptor *id = &iface->altsetting[a];
442 if( id->bInterfaceClass == ifaceClass )
443 return id->bInterfaceNumber;
448 return -1;
452 ///////////////////////////////////////////////////////////////////////////////
453 // Interface
455 Interface::Interface(Device &dev, int iface)
456 : m_dev(dev), m_iface(iface)
458 dout("usb_claim_interface(" << dev.GetHandle()->m_handle << ", 0x" << std::hex << iface << ")");
459 int ret = usb_claim_interface(dev.GetHandle()->m_handle, iface);
460 if( ret < 0 )
461 throw Error(ret, "claim interface failed");
464 Interface::~Interface()
466 dout("usb_release_interface(" << m_dev.GetHandle()->m_handle << ", 0x" << std::hex << m_iface << ")");
467 usb_release_interface(m_dev.GetHandle()->m_handle, m_iface);
471 // SetAltInterface
473 /// Uses the usb_set_altinterface() function to set the currently
474 /// selected USB alternate setting of the current interface.
475 /// The iface parameter passed in should be a value specified
476 /// in the bAlternateSetting descriptor field.
477 /// If unsuccessful, returns false.
479 bool Interface::SetAltInterface(int altSetting)
481 int result = usb_set_altinterface(m_dev.GetHandle()->m_handle, altSetting);
482 m_dev.SetLastError(result);
483 return result >= 0;
486 //////////////////////////////////////////////////////////////////
487 // DeviceDescriptor
489 DeviceDescriptor::DeviceDescriptor(DeviceID& devid)
490 : m_impl(new DeviceDescriptorImpl())
492 if( !devid.m_impl.get() ) {
493 dout("DeviceDescriptor: empty devid");
494 return;
496 // Copy the descriptor over to our memory
497 m_impl->m_dev = devid.m_impl->m_dev;
498 m_impl->m_desc = devid.m_impl->m_dev->descriptor;
499 dout("device_desc loaded"
500 << "\nbLength: " << std::dec << (unsigned int) m_impl->m_desc.bLength
501 << "\nbDescriptorType: " << std::dec << (unsigned int) m_impl->m_desc.bDescriptorType
502 << "\nbcdUSB: 0x" << std::hex << (unsigned int) m_impl->m_desc.bcdUSB
503 << "\nbDeviceClass: " << std::dec << (unsigned int) m_impl->m_desc.bDeviceClass
504 << "\nbDeviceSubClass: " << std::dec << (unsigned int) m_impl->m_desc.bDeviceSubClass
505 << "\nbDeviceProtocol: " << std::dec << (unsigned int) m_impl->m_desc.bDeviceProtocol
506 << "\nbMaxPacketSize0: " << std::dec << (unsigned int) m_impl->m_desc.bMaxPacketSize0
507 << "\nidVendor: 0x" << std::hex << (unsigned int) m_impl->m_desc.idVendor
508 << "\nidProduct: 0x" << std::hex << (unsigned int) m_impl->m_desc.idProduct
509 << "\nbcdDevice: 0x" << std::hex << (unsigned int) m_impl->m_desc.bcdDevice
510 << "\niManufacturer: " << std::dec << (unsigned int) m_impl->m_desc.iManufacturer
511 << "\niProduct: " << std::dec << (unsigned int) m_impl->m_desc.iProduct
512 << "\niSerialNumber: " << std::dec << (unsigned int) m_impl->m_desc.iSerialNumber
513 << "\nbNumConfigurations: " << std::dec << (unsigned int) m_impl->m_desc.bNumConfigurations
514 << "\n"
517 // Create all the configs
518 for( int i = 0; i < m_impl->m_desc.bNumConfigurations; ++i ) {
519 std::auto_ptr<ConfigDescriptor> ptr(new ConfigDescriptor(*this, i));
520 (*this)[ptr->GetNumber()] = ptr.get();
521 ptr.release();
525 DeviceDescriptor::~DeviceDescriptor()
527 // Delete any pointers in the map
528 std::for_each(begin(),
529 end(),
530 deleteMapPtr<int, ConfigDescriptor>);
533 ///////////////////////////////////////////////////////////////////
534 // ConfigDescriptor
536 ConfigDescriptor::ConfigDescriptor(DeviceDescriptor& dev, int cfgnumber)
537 : m_impl(new ConfigDescriptorImpl())
539 // Copy the config descriptor locally
540 m_impl->m_desc = dev.m_impl->m_dev->config[cfgnumber];
541 dout(" config_desc #" << std::dec << cfgnumber << " loaded"
542 << "\nbLength: " << std::dec << (unsigned int) m_impl->m_desc.bLength
543 << "\nbDescriptorType: " << std::dec << (unsigned int) m_impl->m_desc.bDescriptorType
544 << "\nwTotalLength: " << std::dec << (unsigned int) m_impl->m_desc.wTotalLength
545 << "\nbNumInterfaces: " << std::dec << (unsigned int) m_impl->m_desc.bNumInterfaces
546 << "\nbConfigurationValue: " << std::dec << (unsigned int) m_impl->m_desc.bConfigurationValue
547 << "\niConfiguration: " << std::dec << (unsigned int) m_impl->m_desc.iConfiguration
548 << "\nbmAttributes: 0x" << std::hex << (unsigned int) m_impl->m_desc.bmAttributes
549 << "\nMaxPower: " << std::dec << (unsigned int) m_impl->m_desc.MaxPower
550 << "\n"
553 // just for debugging purposes, check for extra descriptors, and
554 // dump them to dout if they exist
555 if( m_impl->m_desc.extra ) {
556 dout("while parsing config descriptor, found a block of extra descriptors:");
557 Barry::Data data(m_impl->m_desc.extra, m_impl->m_desc.extralen);
558 dout(data);
561 // Create all the interfaces
562 for( int i = 0; i < m_impl->m_desc.bNumInterfaces; ++i ) {
563 struct usb_interface* interface = &(m_impl->m_desc.interface[i]);
564 if( !interface->altsetting ) {
565 dout("ConfigDescriptor: empty altsetting");
566 // some devices are buggy and return a higher bNumInterfaces
567 // than the number of interfaces available... in this case
568 // we just skip and continue
569 continue;
571 for( int j = 0; j < interface->num_altsetting; ++j ) {
572 std::auto_ptr<InterfaceDescriptor> ptr(
573 new InterfaceDescriptor(*this, i, j));
574 (*this)[ptr->GetNumber()] = ptr.get();
575 ptr.release();
580 ConfigDescriptor::~ConfigDescriptor()
582 // Delete any pointers in the map
583 std::for_each(begin(),
584 end(),
585 deleteMapPtr<int, InterfaceDescriptor>);
588 uint8_t ConfigDescriptor::GetNumber() const {
589 return m_impl->m_desc.bConfigurationValue;
592 /////////////////////////////////////////////////////////////////////////
593 // InterfaceDescriptor
595 InterfaceDescriptor::InterfaceDescriptor(ConfigDescriptor& cfg,
596 int interface, int altsetting)
597 : m_impl(new InterfaceDescriptorImpl())
599 // Copy the descriptor
600 m_impl->m_desc = cfg.m_impl->m_desc
601 .interface[interface]
602 .altsetting[altsetting];
603 dout(" interface_desc #" << std::dec << interface << " loaded"
604 << "\nbLength: " << std::dec << (unsigned) m_impl->m_desc.bLength
605 << "\nbDescriptorType: " << std::dec << (unsigned) m_impl->m_desc.bDescriptorType
606 << "\nbInterfaceNumber: " << std::dec << (unsigned) m_impl->m_desc.bInterfaceNumber
607 << "\nbAlternateSetting: " << std::dec << (unsigned) m_impl->m_desc.bAlternateSetting
608 << "\nbNumEndpoints: " << std::dec << (unsigned) m_impl->m_desc.bNumEndpoints
609 << "\nbInterfaceClass: " << std::dec << (unsigned) m_impl->m_desc.bInterfaceClass
610 << "\nbInterfaceSubClass: " << std::dec << (unsigned) m_impl->m_desc.bInterfaceSubClass
611 << "\nbInterfaceProtocol: " << std::dec << (unsigned) m_impl->m_desc.bInterfaceProtocol
612 << "\niInterface: " << std::dec << (unsigned) m_impl->m_desc.iInterface
613 << "\n"
616 if( !m_impl->m_desc.endpoint ) {
617 dout("InterfaceDescriptor: empty interface pointer");
618 return;
621 // Create all the endpoints
622 for( int i = 0; i < m_impl->m_desc.bNumEndpoints; ++i ) {
623 std::auto_ptr<EndpointDescriptor> ptr (
624 new EndpointDescriptor(*this, i));
625 this->push_back(ptr.get());
626 ptr.release();
629 // just for debugging purposes, check for extra descriptors, and
630 // dump them to dout if they exist
631 if( m_impl->m_desc.extra ) {
632 dout("while parsing interface descriptor, found a block of extra descriptors:");
633 Barry::Data data(m_impl->m_desc.extra, m_impl->m_desc.extralen);
634 dout(data);
638 InterfaceDescriptor::~InterfaceDescriptor()
640 // Delete any pointers in the vector
641 std::for_each(begin(),
642 end(),
643 deletePtr<EndpointDescriptor>);
646 uint8_t InterfaceDescriptor::GetClass() const
648 return m_impl->m_desc.bInterfaceClass;
651 uint8_t InterfaceDescriptor::GetNumber() const
653 return m_impl->m_desc.bInterfaceNumber;
656 uint8_t InterfaceDescriptor::GetAltSetting() const
658 return m_impl->m_desc.bAlternateSetting;
661 /////////////////////////////////////////////////////////////////////////////////
662 // EndpointDescriptor
664 EndpointDescriptor::EndpointDescriptor(InterfaceDescriptor& interface, int endpoint)
665 : m_impl(new EndpointDescriptorImpl()),
666 m_read(false),
667 m_addr(0),
668 m_type(InvalidType)
670 // Copy the descriptor
671 m_impl->m_desc = interface.m_impl->m_desc.endpoint[endpoint];
672 dout(" endpoint_desc #" << std::dec << endpoint << " loaded"
673 << "\nbLength: " << std::dec << (unsigned ) m_impl->m_desc.bLength
674 << "\nbDescriptorType: " << std::dec << (unsigned ) m_impl->m_desc.bDescriptorType
675 << "\nbEndpointAddress: 0x" << std::hex << (unsigned ) m_impl->m_desc.bEndpointAddress
676 << "\nbmAttributes: 0x" << std::hex << (unsigned ) m_impl->m_desc.bmAttributes
677 << "\nwMaxPacketSize: " << std::dec << (unsigned ) m_impl->m_desc.wMaxPacketSize
678 << "\nbInterval: " << std::dec << (unsigned ) m_impl->m_desc.bInterval
679 << "\nbRefresh: " << std::dec << (unsigned ) m_impl->m_desc.bRefresh
680 << "\nbSynchAddress: " << std::dec << (unsigned ) m_impl->m_desc.bSynchAddress
681 << "\n"
683 // Set up variables
684 m_read = ((m_impl->m_desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0);
685 m_addr = (m_impl->m_desc.bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK);
686 int type = (m_impl->m_desc.bmAttributes & USB_ENDPOINT_TYPE_MASK);
687 m_type = static_cast<Usb::EndpointDescriptor::EpType>(type);
689 // just for debugging purposes, check for extra descriptors, and
690 // dump them to dout if they exist
691 if( m_impl->m_desc.extra ) {
692 dout("while parsing endpoint descriptor, found a block of extra descriptors:");
693 Barry::Data data(m_impl->m_desc.extra, m_impl->m_desc.extralen);
694 dout(data);
698 EndpointDescriptor::~EndpointDescriptor()
702 } // namespace Usb