usbwrap.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       usbwrap.cc
00003 ///             USB API wrapper
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2010, Chris Frey
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 
00023 #include "usbwrap.h"
00024 #include "data.h"
00025 #include "error.h"
00026 #include "debug.h"
00027 
00028 #include <iomanip>
00029 #include <sstream>
00030 #include <errno.h>
00031 #include <string.h>
00032 
00033 #ifndef __DEBUG_MODE__
00034 #define __DEBUG_MODE__
00035 #endif
00036 #include "debug.h"
00037 
00038 namespace Usb {
00039 
00040 ///////////////////////////////////////////////////////////////////////////////
00041 // Usb::Error exception class
00042 
00043 static std::string GetErrorString(int libusb_errcode, const std::string &str)
00044 {
00045         std::ostringstream oss;
00046         oss << "(";
00047 
00048         if( libusb_errcode ) {
00049                 oss << std::setbase(10) << libusb_errcode << ", ";
00050         }
00051 
00052 //      oss << strerror(-libusb_errno) << "): "
00053         oss << usb_strerror() << "): ";
00054         oss << str;
00055         return oss.str();
00056 }
00057 
00058 Error::Error(const std::string &str)
00059         : Barry::Error(GetErrorString(0, str))
00060         , m_libusb_errcode(0)
00061 {
00062 }
00063 
00064 Error::Error(int libusb_errcode, const std::string &str)
00065         : Barry::Error(GetErrorString(libusb_errcode, str))
00066         , m_libusb_errcode(libusb_errcode)
00067 {
00068 }
00069 
00070 
00071 ///////////////////////////////////////////////////////////////////////////////
00072 // Match
00073 
00074 Match::Match(int vendor, int product,
00075                 const char *busname, const char *devname)
00076         : m_busses(0)
00077         , m_dev(0)
00078         , m_vendor(vendor)
00079         , m_product(product)
00080         , m_busname(busname)
00081         , m_devname(devname)
00082 {
00083         usb_find_busses();
00084         usb_find_devices();
00085         m_busses = usb_get_busses();
00086 }
00087 
00088 Match::~Match()
00089 {
00090 }
00091 
00092 bool Match::ToNum(const char *str, long &num)
00093 {
00094         char *end = 0;
00095         num = strtol(str, &end, 10);
00096         return  num >= 0 &&                     // no negative numbers
00097                 num != LONG_MIN && num != LONG_MAX &&   // no overflow
00098                 str != end && *end == '\0';     // whole string valid
00099 }
00100 
00101 //
00102 // Linux treats bus and device path names as numbers, sometimes left
00103 // padded with zeros.  Other platforms, such as Windows, use strings,
00104 // such as "bus-1" or similar.
00105 //
00106 // Here we try to convert each string to a number, and if successful,
00107 // compare them.  If unable to convert, then compare as strings.
00108 // This way, "3" == "003" and "bus-foobar" == "bus-foobar".
00109 //
00110 bool Match::NameCompare(const char *n1, const char *n2)
00111 {
00112         long l1, l2;
00113         if( ToNum(n1, l1) && ToNum(n2, l2) ) {
00114                 return l1 == l2;
00115         }
00116         else {
00117                 return strcmp(n1, n2) == 0;
00118         }
00119 }
00120 
00121 bool Match::next_device(Usb::DeviceIDType *devid)
00122 {
00123         for( ; m_busses; m_busses = m_busses->next ) {
00124 
00125                 // only search on given bus
00126                 if( m_busname && !NameCompare(m_busname, m_busses->dirname) )
00127                         continue;
00128 
00129                 if( !m_dev )
00130                         m_dev = m_busses->devices;
00131 
00132                 for( ; m_dev; m_dev = m_dev->next ) {
00133 
00134                         // search for specific device
00135                         if( m_devname && !NameCompare(m_devname, m_dev->filename) )
00136                                 continue;
00137 
00138                         // is there a match?
00139                         if( m_dev->descriptor.idVendor == m_vendor &&
00140                             m_dev->descriptor.idProduct == m_product ) {
00141                                 // found!
00142                                 *devid = m_dev;
00143 
00144                                 // advance for next time
00145                                 m_dev = m_dev->next;
00146                                 if( !m_dev )
00147                                         m_busses = m_busses->next;
00148 
00149                                 // done
00150                                 return true;
00151                         }
00152                 }
00153         }
00154         return false;
00155 }
00156 
00157 
00158 ///////////////////////////////////////////////////////////////////////////////
00159 // Device
00160 
00161 Device::Device(Usb::DeviceIDType id, int timeout)
00162         : m_id(id),
00163         m_timeout(timeout)
00164 {
00165         dout("usb_open(" << std::dec << id << ")");
00166         m_handle = usb_open(id);
00167         if( !m_handle )
00168                 throw Error("open failed");
00169 }
00170 
00171 Device::~Device()
00172 {
00173         dout("usb_close(" << std::dec << m_handle << ")");
00174         usb_close(m_handle);
00175 }
00176 
00177 bool Device::SetConfiguration(unsigned char cfg)
00178 {
00179         dout("usb_set_configuration(" << std::dec << m_handle << ", 0x" << std::hex << (unsigned int) cfg << ")");
00180         int ret = usb_set_configuration(m_handle, cfg);
00181         m_lasterror = ret;
00182         return ret >= 0;
00183 }
00184 
00185 bool Device::ClearHalt(int ep)
00186 {
00187         dout("usb_clear_halt(" << std::dec << m_handle << ", 0x" << std::hex << ep << ")");
00188         int ret = usb_clear_halt(m_handle, ep);
00189         m_lasterror = ret;
00190         return ret >= 0;
00191 }
00192 
00193 bool Device::Reset()
00194 {
00195         dout("usb_reset(" << std::dec << m_handle << ")");
00196         int ret = usb_reset(m_handle);
00197         m_lasterror = ret;
00198         return ret == 0;
00199 }
00200 
00201 bool Device::BulkRead(int ep, Barry::Data &data, int timeout)
00202 {
00203         int ret;
00204         do {
00205                 data.QuickZap();
00206                 ret = usb_bulk_read(m_handle, ep,
00207                         (char*) data.GetBuffer(), data.GetBufSize(),
00208                         timeout == -1 ? m_timeout : timeout);
00209                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00210                         m_lasterror = ret;
00211                         if( ret == -ETIMEDOUT )
00212                                 throw Timeout(ret, "Timeout in usb_bulk_read");
00213                         else
00214                                 throw Error(ret, "Error in usb_bulk_read");
00215                 }
00216                 data.ReleaseBuffer(ret);
00217         } while( ret == -EINTR || ret == -EAGAIN );
00218 
00219         return ret >= 0;
00220 }
00221 
00222 bool Device::BulkWrite(int ep, const Barry::Data &data, int timeout)
00223 {
00224         ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
00225         int ret;
00226         do {
00227                 ret = usb_bulk_write(m_handle, ep,
00228                         (char*) data.GetData(), data.GetSize(),
00229                         timeout == -1 ? m_timeout : timeout);
00230                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00231                         m_lasterror = ret;
00232                         if( ret == -ETIMEDOUT )
00233                                 throw Timeout(ret, "Timeout in usb_bulk_write (1)");
00234                         else
00235                                 throw Error(ret, "Error in usb_bulk_write (1)");
00236                 }
00237         } while( ret == -EINTR || ret == -EAGAIN );
00238 
00239         return ret >= 0;
00240 }
00241 
00242 bool Device::BulkWrite(int ep, const void *data, size_t size, int timeout)
00243 {
00244 #ifdef __DEBUG_MODE__
00245         Barry::Data dump(data, size);
00246         ddout("BulkWrite to endpoint 0x" << std::hex << ep << ":\n" << dump);
00247 #endif
00248 
00249         int ret;
00250         do {
00251                 ret = usb_bulk_write(m_handle, ep,
00252                         (char*) data, size,
00253                         timeout == -1 ? m_timeout : timeout);
00254                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00255                         m_lasterror = ret;
00256                         if( ret == -ETIMEDOUT )
00257                                 throw Timeout(ret, "Timeout in usb_bulk_write (2)");
00258                         else
00259                                 throw Error(ret, "Error in usb_bulk_write (2)");
00260                 }
00261         } while( ret == -EINTR || ret == -EAGAIN );
00262 
00263         return ret >= 0;
00264 }
00265 
00266 bool Device::InterruptRead(int ep, Barry::Data &data, int timeout)
00267 {
00268         int ret;
00269         do {
00270                 data.QuickZap();
00271                 ret = usb_interrupt_read(m_handle, ep,
00272                         (char*) data.GetBuffer(), data.GetBufSize(),
00273                         timeout == -1 ? m_timeout : timeout);
00274                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00275                         m_lasterror = ret;
00276                         if( ret == -ETIMEDOUT )
00277                                 throw Timeout(ret, "Timeout in usb_interrupt_read");
00278                         else
00279                                 throw Error(ret, "Error in usb_interrupt_read");
00280                 }
00281                 data.ReleaseBuffer(ret);
00282         } while( ret == -EINTR || ret == -EAGAIN );
00283 
00284         return ret >= 0;
00285 }
00286 
00287 bool Device::InterruptWrite(int ep, const Barry::Data &data, int timeout)
00288 {
00289         ddout("InterruptWrite to endpoint 0x" << std::hex << ep << ":\n" << data);
00290 
00291         int ret;
00292         do {
00293                 ret = usb_interrupt_write(m_handle, ep,
00294                         (char*) data.GetData(), data.GetSize(),
00295                         timeout == -1 ? m_timeout : timeout);
00296                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00297                         m_lasterror = ret;
00298                         if( ret == -ETIMEDOUT )
00299                                 throw Timeout(ret, "Timeout in usb_interrupt_write");
00300                         else
00301                                 throw Error(ret, "Error in usb_interrupt_write");
00302                 }
00303         } while( ret == -EINTR || ret == -EAGAIN );
00304 
00305         return ret >= 0;
00306 }
00307 
00308 //
00309 // BulkDrain
00310 //
00311 /// Reads anything available on the given endpoint, with a low timeout,
00312 /// in order to clear any pending reads.
00313 ///
00314 void Device::BulkDrain(int ep, int timeout)
00315 {
00316         try {
00317                 Barry::Data data;
00318                 while( BulkRead(ep, data, timeout) )
00319                 ;
00320         }
00321         catch( Usb::Error & ) {}
00322 }
00323 
00324 //
00325 // GetConfiguration
00326 //
00327 /// Uses the GET_CONFIGURATION control message to determine the currently
00328 /// selected USB configuration, returning it in the cfg argument.
00329 /// If unsuccessful, returns false.
00330 ///
00331 bool Device::GetConfiguration(unsigned char &cfg)
00332 {
00333         int result = usb_control_msg(m_handle, 0x80, USB_REQ_GET_CONFIGURATION, 0, 0,
00334                 (char*) &cfg, 1, m_timeout);
00335         m_lasterror = result;
00336         return result >= 0;
00337 }
00338 
00339 
00340 
00341 ///////////////////////////////////////////////////////////////////////////////
00342 // Interface
00343 
00344 Interface::Interface(Device &dev, int iface)
00345         : m_dev(dev), m_iface(iface)
00346 {
00347         dout("usb_claim_interface(" << dev.GetHandle() << ", 0x" << std::hex << iface << ")");
00348         int ret = usb_claim_interface(dev.GetHandle(), iface);
00349         if( ret < 0 )
00350                 throw Error(ret, "claim interface failed");
00351 
00352         // kernels >= 2.6.28 don't set the interface the same as
00353         // previous versions did, and the Blackberry gets confused
00354         // if it isn't explicitly set
00355         ret = usb_set_altinterface(dev.GetHandle(), iface);
00356         if( ret < 0 )
00357                 throw Error(ret, "set alt interface failed");
00358 }
00359 
00360 Interface::~Interface()
00361 {
00362         dout("usb_release_interface(" << m_dev.GetHandle() << ", 0x" << std::hex << m_iface << ")");
00363         usb_release_interface(m_dev.GetHandle(), m_iface);
00364 }
00365 
00366 
00367 
00368 ///////////////////////////////////////////////////////////////////////////////
00369 // EndpointDiscovery
00370 
00371 bool EndpointDiscovery::Discover(struct usb_interface_descriptor *interface, int epcount)
00372 {
00373         // start fresh
00374         clear();
00375         m_valid = false;
00376 
00377         EndpointPair pair;
00378 
00379         if( !interface || !interface->endpoint ) {
00380                 dout("EndpointDiscovery::Discover: empty interface pointer");
00381                 return false;
00382         }
00383 
00384         for( int i = 0; i < epcount; i++ ) {
00385                 // load descriptor
00386                 usb_endpoint_descriptor desc;
00387                 desc = interface->endpoint[i];
00388                 dout("      endpoint_desc #" << std::dec << i << " loaded"
00389                         << "\nbLength: " << std::dec << (unsigned ) desc.bLength
00390                         << "\nbDescriptorType: " << std::dec << (unsigned ) desc.bDescriptorType
00391                         << "\nbEndpointAddress: 0x" << std::hex << (unsigned ) desc.bEndpointAddress
00392                         << "\nbmAttributes: 0x" << std::hex << (unsigned ) desc.bmAttributes
00393                         << "\nwMaxPacketSize: " << std::dec << (unsigned ) desc.wMaxPacketSize
00394                         << "\nbInterval: " << std::dec << (unsigned ) desc.bInterval
00395                         << "\nbRefresh: " << std::dec << (unsigned ) desc.bRefresh
00396                         << "\nbSynchAddress: " << std::dec << (unsigned ) desc.bSynchAddress
00397                         << "\n"
00398                         );
00399 
00400                 // add to the map
00401                 (*this)[desc.bEndpointAddress] = desc;
00402                 dout("      endpoint added to map with bEndpointAddress: 0x" << std::hex << (unsigned int)desc.bEndpointAddress);
00403 
00404                 // parse the endpoint into read/write sets, if possible,
00405                 // going in discovery order...
00406                 // Assumptions:
00407                 //      - endpoints of related utility will be grouped
00408                 //      - endpoints with same type will be grouped
00409                 //      - endpoints that do not meet the above assumptions
00410                 //              do not belong in a pair
00411                 unsigned char type = desc.bmAttributes & USB_ENDPOINT_TYPE_MASK;
00412                 if( desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK ) {
00413                         // read endpoint
00414                         pair.read = desc.bEndpointAddress;
00415                         dout("        pair.read = 0x" << std::hex << (unsigned int)pair.read);
00416                         if( pair.IsTypeSet() && pair.type != type ) {
00417                                 // if type is already set, we must start over
00418                                 pair.write = 0;
00419                         }
00420                 }
00421                 else {
00422                         // write endpoint
00423                         pair.write = desc.bEndpointAddress;
00424                         dout("        pair.write = 0x" << std::hex << (unsigned int)pair.write);
00425                         if( pair.IsTypeSet() && pair.type != type ) {
00426                                 // if type is already set, we must start over
00427                                 pair.read = 0;
00428                         }
00429                 }
00430                 // save the type last
00431                 pair.type = type;
00432                 dout("        pair.type = 0x" << std::hex << (unsigned int)pair.type);
00433 
00434                 // if pair is complete, add to array
00435                 if( pair.IsComplete() ) {
00436                         m_endpoints.push_back(pair);
00437                         dout("        pair added! ("
00438                                 << "read: 0x" << std::hex << (unsigned int)pair.read << ","
00439                                 << "write: 0x" << std::hex << (unsigned int)pair.write << ","
00440                                 << "type: 0x" << std::hex << (unsigned int)pair.type << ")");
00441                         pair = EndpointPair();  // clear
00442                 }
00443         }
00444 
00445         // just for debugging purposes, check for extra descriptors, and
00446         // dump them to dout if they exist
00447         if( interface->extra ) {
00448                 dout("while parsing endpoints, found a block of extra descriptors:");
00449                 Barry::Data data(interface->extra, interface->extralen);
00450                 dout(data);
00451         }
00452 
00453         return m_valid = true;
00454 }
00455 
00456 
00457 ///////////////////////////////////////////////////////////////////////////////
00458 // InterfaceDiscovery
00459 
00460 bool InterfaceDiscovery::DiscoverInterface(struct usb_interface *interface)
00461 {
00462         if( !interface->altsetting ) {
00463                 dout("InterfaceDiscovery::DiscoverIterface: empty altsetting");
00464                 // some devices are buggy and return a higher bNumInterfaces
00465                 // than the number of interfaces available... in this case
00466                 // we just skip and continue
00467                 return true;
00468         }
00469 
00470         for( int i = 0; i < interface->num_altsetting; i++ ) {
00471                 // load descriptor
00472                 InterfaceDesc desc;
00473                 desc.desc = interface->altsetting[i];
00474                 dout("    interface_desc #" << std::dec << i << " loaded"
00475                         << "\nbLength: " << std::dec << (unsigned) desc.desc.bLength
00476                         << "\nbDescriptorType: " << std::dec << (unsigned) desc.desc.bDescriptorType
00477                         << "\nbInterfaceNumber: " << std::dec << (unsigned) desc.desc.bInterfaceNumber
00478                         << "\nbAlternateSetting: " << std::dec << (unsigned) desc.desc.bAlternateSetting
00479                         << "\nbNumEndpoints: " << std::dec << (unsigned) desc.desc.bNumEndpoints
00480                         << "\nbInterfaceClass: " << std::dec << (unsigned) desc.desc.bInterfaceClass
00481                         << "\nbInterfaceSubClass: " << std::dec << (unsigned) desc.desc.bInterfaceSubClass
00482                         << "\nbInterfaceProtocol: " << std::dec << (unsigned) desc.desc.bInterfaceProtocol
00483                         << "\niInterface: " << std::dec << (unsigned) desc.desc.iInterface
00484                         << "\n"
00485                         );
00486 
00487                 // load all endpoints on this interface
00488                 if( !desc.endpoints.Discover(&desc.desc, desc.desc.bNumEndpoints) ) {
00489                         dout("    endpoint discovery failed for bInterfaceNumber: " << std::dec << (unsigned int)desc.desc.bInterfaceNumber << ", not added to map.");
00490                         return false;
00491                 }
00492 
00493                 // add to the map
00494                 (*this)[desc.desc.bInterfaceNumber] = desc;
00495                 dout("    interface added to map with bInterfaceNumber: " << std::dec << (unsigned int)desc.desc.bInterfaceNumber);
00496         }
00497         return true;
00498 }
00499 
00500 bool InterfaceDiscovery::Discover(Usb::DeviceIDType devid, int cfgidx, int ifcount)
00501 {
00502         // start fresh
00503         clear();
00504         m_valid = false;
00505 
00506         if( !devid || !devid->config || !devid->config[cfgidx].interface ) {
00507                 dout("InterfaceDiscovery::Discover: empty devid/config/interface");
00508                 return false;
00509         }
00510 
00511         for( int i = 0; i < ifcount; i++ ) {
00512                 if( !DiscoverInterface(&devid->config[cfgidx].interface[i]) )
00513                         return false;
00514         }
00515 
00516         return m_valid = true;
00517 }
00518 
00519 
00520 ///////////////////////////////////////////////////////////////////////////////
00521 // ConfigDiscovery
00522 
00523 bool ConfigDiscovery::Discover(Usb::DeviceIDType devid, int cfgcount)
00524 {
00525         // start fresh
00526         clear();
00527         m_valid = false;
00528 
00529         for( int i = 0; i < cfgcount; i++ ) {
00530                 // load descriptor
00531                 ConfigDesc desc;
00532                 if( !devid || !devid->config ) {
00533                         dout("ConfigDiscovery::Discover: empty devid or config");
00534                         return false;
00535                 }
00536                 desc.desc = devid->config[i];
00537                 dout("  config_desc #" << std::dec << i << " loaded"
00538                         << "\nbLength: " << std::dec << (unsigned int) desc.desc.bLength
00539                         << "\nbDescriptorType: " << std::dec << (unsigned int) desc.desc.bDescriptorType
00540                         << "\nwTotalLength: " << std::dec << (unsigned int) desc.desc.wTotalLength
00541                         << "\nbNumInterfaces: " << std::dec << (unsigned int) desc.desc.bNumInterfaces
00542                         << "\nbConfigurationValue: " << std::dec << (unsigned int) desc.desc.bConfigurationValue
00543                         << "\niConfiguration: " << std::dec << (unsigned int) desc.desc.iConfiguration
00544                         << "\nbmAttributes: 0x" << std::hex << (unsigned int) desc.desc.bmAttributes
00545                         << "\nMaxPower: " << std::dec << (unsigned int) desc.desc.MaxPower
00546                         << "\n"
00547                         );
00548 
00549                 // just for debugging purposes, check for extra descriptors, and
00550                 // dump them to dout if they exist
00551                 if( desc.desc.extra ) {
00552                         dout("while parsing config descriptor, found a block of extra descriptors:");
00553                         Barry::Data data(desc.desc.extra, desc.desc.extralen);
00554                         dout(data);
00555                 }
00556 
00557                 // load all interfaces on this configuration
00558                 if( !desc.interfaces.Discover(devid, i, desc.desc.bNumInterfaces) ) {
00559                         dout("  config discovery failed for bConfigurationValue: " << std::dec << (unsigned int)desc.desc.bConfigurationValue << ", not added to map.");
00560                         return false;
00561                 }
00562 
00563                 // add to the map
00564                 (*this)[desc.desc.bConfigurationValue] = desc;
00565                 dout("  config added to map with bConfigurationValue: " << std::dec << (unsigned int)desc.desc.bConfigurationValue);
00566         }
00567 
00568         return m_valid = true;
00569 }
00570 
00571 
00572 ///////////////////////////////////////////////////////////////////////////////
00573 // DeviceDiscovery
00574 
00575 DeviceDiscovery::DeviceDiscovery(Usb::DeviceIDType devid)
00576         : m_valid(false)
00577 {
00578         Discover(devid);
00579 }
00580 
00581 bool DeviceDiscovery::Discover(Usb::DeviceIDType devid)
00582 {
00583         // start fresh
00584         configs.clear();
00585         m_valid = false;
00586 
00587         // copy the descriptor over to our memory
00588         if( !devid ) {
00589                 dout("DeviceDiscovery::Discover: empty devid");
00590                 return false;
00591         }
00592 
00593         desc = devid->descriptor;
00594         dout("device_desc loaded"
00595                 << "\nbLength: " << std::dec << (unsigned int) desc.bLength
00596                 << "\nbDescriptorType: " << std::dec << (unsigned int) desc.bDescriptorType
00597                 << "\nbcdUSB: 0x" << std::hex << (unsigned int) desc.bcdUSB
00598                 << "\nbDeviceClass: " << std::dec << (unsigned int) desc.bDeviceClass
00599                 << "\nbDeviceSubClass: " << std::dec << (unsigned int) desc.bDeviceSubClass
00600                 << "\nbDeviceProtocol: " << std::dec << (unsigned int) desc.bDeviceProtocol
00601                 << "\nbMaxPacketSize0: " << std::dec << (unsigned int) desc.bMaxPacketSize0
00602                 << "\nidVendor: 0x" << std::hex << (unsigned int) desc.idVendor
00603                 << "\nidProduct: 0x" << std::hex << (unsigned int) desc.idProduct
00604                 << "\nbcdDevice: 0x" << std::hex << (unsigned int) desc.bcdDevice
00605                 << "\niManufacturer: " << std::dec << (unsigned int) desc.iManufacturer
00606                 << "\niProduct: " << std::dec << (unsigned int) desc.iProduct
00607                 << "\niSerialNumber: " << std::dec << (unsigned int) desc.iSerialNumber
00608                 << "\nbNumConfigurations: " << std::dec << (unsigned int) desc.bNumConfigurations
00609                 << "\n"
00610         );
00611 
00612         m_valid = configs.Discover(devid, desc.bNumConfigurations);
00613         return m_valid;
00614 }
00615 
00616 } // namespace Usb
00617 
Generated by  doxygen 1.6.2-20100208