rpm: removed .la files, and fixed library packaging
[barry/progweb.git] / src / usbwrap_libusb.cc
blob9c69fc84c604add7bd82fd2911c67e8bacf56570
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 <algorithm>
32 #ifndef __DEBUG_MODE__
33 #define __DEBUG_MODE__
34 #endif
35 #include "debug.h"
37 namespace Usb {
39 // helper function to make deleting pointers in maps and vectors easier
40 template<typename T> static void deletePtr(T* ptr) {
41 delete ptr;
44 template<typename K, typename T> static void deleteMapPtr(std::pair<K,T*> ptr) {
45 delete ptr.second;
48 ///////////////////////////////////////////////////////////////////////////////
49 // Static functions
51 std::string LibraryInterface::GetLastErrorString(int /*libusb_errcode*/)
53 // Errcode is unused by libusb, so just call the last error
54 return std::string(usb_strerror());
57 int LibraryInterface::TranslateErrcode(int libusb_errcode)
59 // libusb errcode == system errcode
60 return libusb_errcode;
63 int LibraryInterface::Init()
65 // if the environment variable USB_DEBUG is set, that
66 // level value will be used instead of our 9 below...
67 // if you need to *force* this to 9, call SetDataDump(true)
68 // after Init()
69 usb_init();
70 // Can never fail, so return success
71 return 0;
74 void LibraryInterface::Uninit()
76 // Nothing to do
79 void LibraryInterface::SetDataDump(bool data_dump_mode)
81 if( data_dump_mode )
82 usb_set_debug(9);
83 else
84 usb_set_debug(0);
87 ///////////////////////////////////////////////////////////////////////////////
88 // DeviceID
90 DeviceID::DeviceID(DeviceIDImpl* impl)
91 : m_impl(impl)
95 DeviceID::~DeviceID()
99 const char* DeviceID::GetBusName() const
101 return m_impl->m_dev->bus->dirname;
104 uint16_t DeviceID::GetNumber() const
106 return m_impl->m_dev->devnum;
109 const char* DeviceID::GetFilename() const
111 return m_impl->m_dev->filename;
114 uint16_t DeviceID::GetIdProduct() const
116 return m_impl->m_dev->descriptor.idProduct;
119 ///////////////////////////////////////////////////////////////////////////////
120 // DeviceList
122 DeviceList::DeviceList()
123 : m_impl(new DeviceListImpl())
125 // Work out what devices are on the bus at the moment
126 usb_find_busses();
127 usb_find_devices();
128 struct usb_bus* busses = usb_get_busses();
129 for( ; busses; busses = busses->next ) {
130 struct usb_device* dev = busses->devices;
131 for( ; dev; dev = dev->next ) {
132 // Add the device to the list of devices
133 std::auto_ptr<DeviceIDImpl> impl( new DeviceIDImpl() );
134 impl->m_dev = dev;
135 DeviceID devID(impl.release());
136 m_impl->m_devices.push_back(devID);
141 DeviceList::~DeviceList()
146 static bool ToNum(const char *str, long &num)
148 char *end = 0;
149 num = strtol(str, &end, 10);
150 return num >= 0 && // no negative numbers
151 num != LONG_MIN && num != LONG_MAX && // no overflow
152 str != end && *end == '\0'; // whole string valid
156 // Linux treats bus and device path names as numbers, sometimes left
157 // padded with zeros. Other platforms, such as Windows, use strings,
158 // such as "bus-1" or similar.
160 // Here we try to convert each string to a number, and if successful,
161 // compare them. If unable to convert, then compare as strings.
162 // This way, "3" == "003" and "bus-foobar" == "bus-foobar".
164 static bool NameCompare(const char *n1, const char *n2)
166 long l1, l2;
167 if( ToNum(n1, l1) && ToNum(n2, l2) ) {
168 return l1 == l2;
170 else {
171 return strcmp(n1, n2) == 0;
175 std::vector<DeviceID> DeviceList::MatchDevices(int vendor, int product,
176 const char *busname, const char *devname)
178 std::vector<DeviceID> ret;
180 std::vector<DeviceID>::iterator iter = m_impl->m_devices.begin();
182 for( ; iter != m_impl->m_devices.end() ; ++iter ) {
183 struct usb_device* dev = iter->m_impl->m_dev;
185 // only search on given bus
186 if( busname && !NameCompare(busname, dev->bus->dirname) )
187 continue;
189 // search for specific device
190 if( devname && !NameCompare(devname, dev->filename) )
191 continue;
193 // is there a match?
194 if( dev->descriptor.idVendor == vendor &&
195 ( dev->descriptor.idProduct == product ||
196 product == PRODUCT_ANY )) {
197 ret.push_back(*iter);
201 return ret;
204 ///////////////////////////////////////////////////////////////////////////////
205 // Device
207 Device::Device(const Usb::DeviceID& id, int timeout)
208 : m_id(id),
209 m_timeout(timeout)
211 dout("usb_open(" << std::dec << id.m_impl.get() << ")");
212 if( !id.m_impl.get() )
213 throw Error("invalid USB device ID");
214 m_handle.reset(new DeviceHandle());
215 m_handle->m_handle = usb_open(id.m_impl->m_dev);
216 if( !m_handle->m_handle )
217 throw Error("open failed");
220 Device::~Device()
222 dout("usb_close(" << std::dec << m_handle->m_handle << ")");
223 usb_close(m_handle->m_handle);
226 bool Device::SetConfiguration(unsigned char cfg)
228 dout("usb_set_configuration(" << std::dec << m_handle->m_handle << ", 0x" << std::hex << (unsigned int) cfg << ")");
229 int ret = usb_set_configuration(m_handle->m_handle, cfg);
230 m_lasterror = ret;
231 return ret >= 0;
234 bool Device::ClearHalt(int ep)
236 dout("usb_clear_halt(" << std::dec << m_handle->m_handle << ", 0x" << std::hex << ep << ")");
237 int ret = usb_clear_halt(m_handle->m_handle, ep);
238 m_lasterror = ret;
239 return ret >= 0;
242 bool Device::Reset()
244 dout("usb_reset(" << std::dec << m_handle->m_handle << ")");
245 int ret = usb_reset(m_handle->m_handle);
246 m_lasterror = ret;
247 return ret == 0;
250 bool Device::BulkRead(int ep, Barry::Data &data, int timeout)
252 int ret;
253 do {
254 data.QuickZap();
255 ret = usb_bulk_read(m_handle->m_handle, ep,
256 (char*) data.GetBuffer(), data.GetBufSize(),
257 timeout == -1 ? m_timeout : timeout);
258 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
259 m_lasterror = ret;
260 if( ret == -ETIMEDOUT )
261 throw Timeout(ret, "Timeout in usb_bulk_read");
262 else
263 throw Error(ret, "Error in usb_bulk_read");
265 else if( ret > 0 )
266 data.ReleaseBuffer(ret);
267 } while( ret == -EINTR || ret == -EAGAIN );
269 return ret >= 0;
272 bool Device::BulkWrite(int ep, const Barry::Data &data, int timeout)
274 ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
275 int ret;
276 do {
277 ret = usb_bulk_write(m_handle->m_handle, ep,
278 (char*) data.GetData(), data.GetSize(),
279 timeout == -1 ? m_timeout : timeout);
280 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
281 m_lasterror = ret;
282 if( ret == -ETIMEDOUT )
283 throw Timeout(ret, "Timeout in usb_bulk_write (1)");
284 else
285 throw Error(ret, "Error in usb_bulk_write (1)");
287 } while( ret == -EINTR || ret == -EAGAIN );
289 return ret >= 0;
292 bool Device::BulkWrite(int ep, const void *data, size_t size, int timeout)
294 #ifdef __DEBUG_MODE__
295 Barry::Data dump(data, size);
296 ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << dump);
297 #endif
299 int ret;
300 do {
301 ret = usb_bulk_write(m_handle->m_handle, ep,
302 (char*) data, size,
303 timeout == -1 ? m_timeout : timeout);
304 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
305 m_lasterror = ret;
306 if( ret == -ETIMEDOUT )
307 throw Timeout(ret, "Timeout in usb_bulk_write (2)");
308 else
309 throw Error(ret, "Error in usb_bulk_write (2)");
311 } while( ret == -EINTR || ret == -EAGAIN );
313 return ret >= 0;
316 bool Device::InterruptRead(int ep, Barry::Data &data, int timeout)
318 int ret;
319 do {
320 data.QuickZap();
321 ret = usb_interrupt_read(m_handle->m_handle, ep,
322 (char*) data.GetBuffer(), data.GetBufSize(),
323 timeout == -1 ? m_timeout : timeout);
324 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
325 m_lasterror = ret;
326 if( ret == -ETIMEDOUT )
327 throw Timeout(ret, "Timeout in usb_interrupt_read");
328 else
329 throw Error(ret, "Error in usb_interrupt_read");
331 else if( ret > 0 )
332 data.ReleaseBuffer(ret);
333 } while( ret == -EINTR || ret == -EAGAIN );
335 return ret >= 0;
338 bool Device::InterruptWrite(int ep, const Barry::Data &data, int timeout)
340 ddout("InterruptWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
342 int ret;
343 do {
344 ret = usb_interrupt_write(m_handle->m_handle, ep,
345 (char*) data.GetData(), data.GetSize(),
346 timeout == -1 ? m_timeout : timeout);
347 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
348 m_lasterror = ret;
349 if( ret == -ETIMEDOUT )
350 throw Timeout(ret, "Timeout in usb_interrupt_write");
351 else
352 throw Error(ret, "Error in usb_interrupt_write");
354 } while( ret == -EINTR || ret == -EAGAIN );
356 return ret >= 0;
360 // BulkDrain
362 /// Reads anything available on the given endpoint, with a low timeout,
363 /// in order to clear any pending reads.
365 void Device::BulkDrain(int ep, int timeout)
367 try {
368 Barry::Data data;
369 while( BulkRead(ep, data, timeout) )
372 catch( Usb::Error & ) {}
376 // GetConfiguration
378 /// Uses the GET_CONFIGURATION control message to determine the currently
379 /// selected USB configuration, returning it in the cfg argument.
380 /// If unsuccessful, returns false.
382 bool Device::GetConfiguration(unsigned char &cfg)
384 int result = usb_control_msg(m_handle->m_handle, 0x80, USB_REQ_GET_CONFIGURATION, 0, 0,
385 (char*) &cfg, 1, m_timeout);
386 m_lasterror = result;
387 return result >= 0;
390 // Returns the current power level of the device, or 0 if unknown
391 int Device::GetPowerLevel()
393 if( !m_id.m_impl->m_dev->config ||
394 !m_id.m_impl->m_dev->descriptor.bNumConfigurations < 1 )
395 return 0;
397 return m_id.m_impl->m_dev->config[0].MaxPower;
400 // Requests that the kernel driver is detached, returning false on failure
401 bool Device::DetachKernelDriver(int iface)
403 #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
404 int result = usb_detach_kernel_driver_np(m_handle->m_handle, iface);
405 m_lasterror = result;
406 return result >= 0;
407 #else
408 m_lasterror = -ENOSYS;
409 return false;
410 #endif
413 // Sends a control message to the device, returning false on failure
414 bool Device::ControlMsg(int requesttype, int request, int value,
415 int index, char *bytes, int size, int timeout)
417 int result = usb_control_msg(m_handle->m_handle,
418 requesttype, request, value, index,
419 bytes, size, timeout);
420 m_lasterror = result;
421 return result >= 0;
425 int Device::FindInterface(int ifaceClass)
427 struct usb_config_descriptor *cfg = m_id.m_impl->m_dev->config;
429 if( cfg ) {
431 for( unsigned i = 0; cfg->interface && i < cfg->bNumInterfaces; i++ ) {
432 struct usb_interface *iface = &cfg->interface[i];
433 for( int a = 0; iface->altsetting && a < iface->num_altsetting; a++ ) {
434 struct usb_interface_descriptor *id = &iface->altsetting[a];
435 if( id->bInterfaceClass == ifaceClass )
436 return id->bInterfaceNumber;
441 return -1;
445 ///////////////////////////////////////////////////////////////////////////////
446 // Interface
448 Interface::Interface(Device &dev, int iface)
449 : m_dev(dev), m_iface(iface)
451 dout("usb_claim_interface(" << dev.GetHandle()->m_handle << ", 0x" << std::hex << iface << ")");
452 int ret = usb_claim_interface(dev.GetHandle()->m_handle, iface);
453 if( ret < 0 )
454 throw Error(ret, "claim interface failed");
457 Interface::~Interface()
459 dout("usb_release_interface(" << m_dev.GetHandle()->m_handle << ", 0x" << std::hex << m_iface << ")");
460 usb_release_interface(m_dev.GetHandle()->m_handle, m_iface);
464 // SetAltInterface
466 /// Uses the usb_set_altinterface() function to set the currently
467 /// selected USB alternate setting of the current interface.
468 /// The iface parameter passed in should be a value specified
469 /// in the bAlternateSetting descriptor field.
470 /// If unsuccessful, returns false.
472 bool Interface::SetAltInterface(int altSetting)
474 int result = usb_set_altinterface(m_dev.GetHandle()->m_handle, altSetting);
475 m_dev.SetLastError(result);
476 return result >= 0;
479 //////////////////////////////////////////////////////////////////
480 // DeviceDescriptor
482 DeviceDescriptor::DeviceDescriptor(DeviceID& devid)
483 : m_impl(new DeviceDescriptorImpl())
485 if( !devid.m_impl.get() ) {
486 dout("DeviceDescriptor: empty devid");
487 return;
489 // Copy the descriptor over to our memory
490 m_impl->m_dev = devid.m_impl->m_dev;
491 m_impl->m_desc = devid.m_impl->m_dev->descriptor;
492 dout("device_desc loaded"
493 << "\nbLength: " << std::dec << (unsigned int) m_impl->m_desc.bLength
494 << "\nbDescriptorType: " << std::dec << (unsigned int) m_impl->m_desc.bDescriptorType
495 << "\nbcdUSB: 0x" << std::hex << (unsigned int) m_impl->m_desc.bcdUSB
496 << "\nbDeviceClass: " << std::dec << (unsigned int) m_impl->m_desc.bDeviceClass
497 << "\nbDeviceSubClass: " << std::dec << (unsigned int) m_impl->m_desc.bDeviceSubClass
498 << "\nbDeviceProtocol: " << std::dec << (unsigned int) m_impl->m_desc.bDeviceProtocol
499 << "\nbMaxPacketSize0: " << std::dec << (unsigned int) m_impl->m_desc.bMaxPacketSize0
500 << "\nidVendor: 0x" << std::hex << (unsigned int) m_impl->m_desc.idVendor
501 << "\nidProduct: 0x" << std::hex << (unsigned int) m_impl->m_desc.idProduct
502 << "\nbcdDevice: 0x" << std::hex << (unsigned int) m_impl->m_desc.bcdDevice
503 << "\niManufacturer: " << std::dec << (unsigned int) m_impl->m_desc.iManufacturer
504 << "\niProduct: " << std::dec << (unsigned int) m_impl->m_desc.iProduct
505 << "\niSerialNumber: " << std::dec << (unsigned int) m_impl->m_desc.iSerialNumber
506 << "\nbNumConfigurations: " << std::dec << (unsigned int) m_impl->m_desc.bNumConfigurations
507 << "\n"
510 // Create all the configs
511 for( int i = 0; i < m_impl->m_desc.bNumConfigurations; ++i ) {
512 std::auto_ptr<ConfigDescriptor> ptr(new ConfigDescriptor(*this, i));
513 (*this)[ptr->GetNumber()] = ptr.get();
514 ptr.release();
518 DeviceDescriptor::~DeviceDescriptor()
520 // Delete any pointers in the map
521 std::for_each(begin(),
522 end(),
523 deleteMapPtr<int, ConfigDescriptor>);
526 ///////////////////////////////////////////////////////////////////
527 // ConfigDescriptor
529 ConfigDescriptor::ConfigDescriptor(DeviceDescriptor& dev, int cfgnumber)
530 : m_impl(new ConfigDescriptorImpl())
532 // Copy the config descriptor locally
533 m_impl->m_desc = dev.m_impl->m_dev->config[cfgnumber];
534 dout(" config_desc #" << std::dec << cfgnumber << " loaded"
535 << "\nbLength: " << std::dec << (unsigned int) m_impl->m_desc.bLength
536 << "\nbDescriptorType: " << std::dec << (unsigned int) m_impl->m_desc.bDescriptorType
537 << "\nwTotalLength: " << std::dec << (unsigned int) m_impl->m_desc.wTotalLength
538 << "\nbNumInterfaces: " << std::dec << (unsigned int) m_impl->m_desc.bNumInterfaces
539 << "\nbConfigurationValue: " << std::dec << (unsigned int) m_impl->m_desc.bConfigurationValue
540 << "\niConfiguration: " << std::dec << (unsigned int) m_impl->m_desc.iConfiguration
541 << "\nbmAttributes: 0x" << std::hex << (unsigned int) m_impl->m_desc.bmAttributes
542 << "\nMaxPower: " << std::dec << (unsigned int) m_impl->m_desc.MaxPower
543 << "\n"
546 // just for debugging purposes, check for extra descriptors, and
547 // dump them to dout if they exist
548 if( m_impl->m_desc.extra ) {
549 dout("while parsing config descriptor, found a block of extra descriptors:");
550 Barry::Data data(m_impl->m_desc.extra, m_impl->m_desc.extralen);
551 dout(data);
554 // Create all the interfaces
555 for( int i = 0; i < m_impl->m_desc.bNumInterfaces; ++i ) {
556 struct usb_interface* interface = &(m_impl->m_desc.interface[i]);
557 if( !interface->altsetting ) {
558 dout("ConfigDescriptor: empty altsetting");
559 // some devices are buggy and return a higher bNumInterfaces
560 // than the number of interfaces available... in this case
561 // we just skip and continue
562 continue;
564 for( int j = 0; j < interface->num_altsetting; ++j ) {
565 std::auto_ptr<InterfaceDescriptor> ptr(
566 new InterfaceDescriptor(*this, i, j));
567 (*this)[ptr->GetNumber()] = ptr.get();
568 ptr.release();
573 ConfigDescriptor::~ConfigDescriptor()
575 // Delete any pointers in the map
576 std::for_each(begin(),
577 end(),
578 deleteMapPtr<int, InterfaceDescriptor>);
581 uint8_t ConfigDescriptor::GetNumber() const {
582 return m_impl->m_desc.bConfigurationValue;
585 /////////////////////////////////////////////////////////////////////////
586 // InterfaceDescriptor
588 InterfaceDescriptor::InterfaceDescriptor(ConfigDescriptor& cfg,
589 int interface, int altsetting)
590 : m_impl(new InterfaceDescriptorImpl())
592 // Copy the descriptor
593 m_impl->m_desc = cfg.m_impl->m_desc
594 .interface[interface]
595 .altsetting[altsetting];
596 dout(" interface_desc #" << std::dec << interface << " loaded"
597 << "\nbLength: " << std::dec << (unsigned) m_impl->m_desc.bLength
598 << "\nbDescriptorType: " << std::dec << (unsigned) m_impl->m_desc.bDescriptorType
599 << "\nbInterfaceNumber: " << std::dec << (unsigned) m_impl->m_desc.bInterfaceNumber
600 << "\nbAlternateSetting: " << std::dec << (unsigned) m_impl->m_desc.bAlternateSetting
601 << "\nbNumEndpoints: " << std::dec << (unsigned) m_impl->m_desc.bNumEndpoints
602 << "\nbInterfaceClass: " << std::dec << (unsigned) m_impl->m_desc.bInterfaceClass
603 << "\nbInterfaceSubClass: " << std::dec << (unsigned) m_impl->m_desc.bInterfaceSubClass
604 << "\nbInterfaceProtocol: " << std::dec << (unsigned) m_impl->m_desc.bInterfaceProtocol
605 << "\niInterface: " << std::dec << (unsigned) m_impl->m_desc.iInterface
606 << "\n"
609 if( !m_impl->m_desc.endpoint ) {
610 dout("InterfaceDescriptor: empty interface pointer");
611 return;
614 // Create all the endpoints
615 for( int i = 0; i < m_impl->m_desc.bNumEndpoints; ++i ) {
616 std::auto_ptr<EndpointDescriptor> ptr (
617 new EndpointDescriptor(*this, i));
618 this->push_back(ptr.get());
619 ptr.release();
622 // just for debugging purposes, check for extra descriptors, and
623 // dump them to dout if they exist
624 if( m_impl->m_desc.extra ) {
625 dout("while parsing interface descriptor, found a block of extra descriptors:");
626 Barry::Data data(m_impl->m_desc.extra, m_impl->m_desc.extralen);
627 dout(data);
631 InterfaceDescriptor::~InterfaceDescriptor()
633 // Delete any pointers in the vector
634 std::for_each(begin(),
635 end(),
636 deletePtr<EndpointDescriptor>);
639 uint8_t InterfaceDescriptor::GetClass() const
641 return m_impl->m_desc.bInterfaceClass;
644 uint8_t InterfaceDescriptor::GetNumber() const
646 return m_impl->m_desc.bInterfaceNumber;
649 uint8_t InterfaceDescriptor::GetAltSetting() const
651 return m_impl->m_desc.bAlternateSetting;
654 /////////////////////////////////////////////////////////////////////////////////
655 // EndpointDescriptor
657 EndpointDescriptor::EndpointDescriptor(InterfaceDescriptor& interface, int endpoint)
658 : m_impl(new EndpointDescriptorImpl()),
659 m_read(false),
660 m_addr(0),
661 m_type(InvalidType)
663 // Copy the descriptor
664 m_impl->m_desc = interface.m_impl->m_desc.endpoint[endpoint];
665 dout(" endpoint_desc #" << std::dec << endpoint << " loaded"
666 << "\nbLength: " << std::dec << (unsigned ) m_impl->m_desc.bLength
667 << "\nbDescriptorType: " << std::dec << (unsigned ) m_impl->m_desc.bDescriptorType
668 << "\nbEndpointAddress: 0x" << std::hex << (unsigned ) m_impl->m_desc.bEndpointAddress
669 << "\nbmAttributes: 0x" << std::hex << (unsigned ) m_impl->m_desc.bmAttributes
670 << "\nwMaxPacketSize: " << std::dec << (unsigned ) m_impl->m_desc.wMaxPacketSize
671 << "\nbInterval: " << std::dec << (unsigned ) m_impl->m_desc.bInterval
672 << "\nbRefresh: " << std::dec << (unsigned ) m_impl->m_desc.bRefresh
673 << "\nbSynchAddress: " << std::dec << (unsigned ) m_impl->m_desc.bSynchAddress
674 << "\n"
676 // Set up variables
677 m_read = ((m_impl->m_desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) != 0);
678 m_addr = (m_impl->m_desc.bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK);
679 int type = (m_impl->m_desc.bmAttributes & USB_ENDPOINT_TYPE_MASK);
680 m_type = static_cast<Usb::EndpointDescriptor::EpType>(type);
682 // just for debugging purposes, check for extra descriptors, and
683 // dump them to dout if they exist
684 if( m_impl->m_desc.extra ) {
685 dout("while parsing endpoint descriptor, found a block of extra descriptors:");
686 Barry::Data data(m_impl->m_desc.extra, m_impl->m_desc.extralen);
687 dout(data);
691 EndpointDescriptor::~EndpointDescriptor()
695 } // namespace Usb