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-2009, 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 << "," << std::dec << (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 << "," << std::dec << 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                 ret = usb_bulk_read(m_handle, ep,
00206                         (char*) data.GetBuffer(), data.GetBufSize(),
00207                         timeout == -1 ? m_timeout : timeout);
00208                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00209                         m_lasterror = ret;
00210                         if( ret == -ETIMEDOUT )
00211                                 throw Timeout(ret, "Timeout in usb_bulk_read");
00212                         else
00213                                 throw Error(ret, "Error in usb_bulk_read");
00214                 }
00215                 data.ReleaseBuffer(ret);
00216         } while( ret == -EINTR || ret == -EAGAIN );
00217 
00218         return ret >= 0;
00219 }
00220 
00221 bool Device::BulkWrite(int ep, const Barry::Data &data, int timeout)
00222 {
00223         ddout("BulkWrite to endpoint " << std::dec << ep << ":\n" << data);
00224         int ret;
00225         do {
00226                 ret = usb_bulk_write(m_handle, ep,
00227                         (char*) data.GetData(), data.GetSize(),
00228                         timeout == -1 ? m_timeout : timeout);
00229                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00230                         m_lasterror = ret;
00231                         if( ret == -ETIMEDOUT )
00232                                 throw Timeout(ret, "Timeout in usb_bulk_write");
00233                         else
00234                                 throw Error(ret, "Error in usb_bulk_write");
00235                 }
00236         } while( ret == -EINTR || ret == -EAGAIN );
00237 
00238         return ret >= 0;
00239 }
00240 
00241 bool Device::BulkWrite(int ep, const void *data, size_t size, int timeout)
00242 {
00243 #ifdef __DEBUG_MODE__
00244         Barry::Data dump(data, size);
00245         ddout("BulkWrite to endpoint " << std::dec << ep << ":\n" << dump);
00246 #endif
00247 
00248         int ret;
00249         do {
00250                 ret = usb_bulk_write(m_handle, ep,
00251                         (char*) data, size,
00252                         timeout == -1 ? m_timeout : timeout);
00253                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00254                         m_lasterror = ret;
00255                         if( ret == -ETIMEDOUT )
00256                                 throw Timeout(ret, "Timeout in usb_bulk_read");
00257                         else
00258                                 throw Error(ret, "Error in usb_bulk_read");
00259                 }
00260         } while( ret == -EINTR || ret == -EAGAIN );
00261 
00262         return ret >= 0;
00263 }
00264 
00265 bool Device::InterruptRead(int ep, Barry::Data &data, int timeout)
00266 {
00267         int ret;
00268         do {
00269                 ret = usb_interrupt_read(m_handle, ep,
00270                         (char*) data.GetBuffer(), data.GetBufSize(),
00271                         timeout == -1 ? m_timeout : timeout);
00272                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00273                         m_lasterror = ret;
00274                         if( ret == -ETIMEDOUT )
00275                                 throw Timeout(ret, "Timeout in usb_bulk_read");
00276                         else
00277                                 throw Error(ret, "Error in usb_bulk_read");
00278                 }
00279                 data.ReleaseBuffer(ret);
00280         } while( ret == -EINTR || ret == -EAGAIN );
00281 
00282         return ret >= 0;
00283 }
00284 
00285 bool Device::InterruptWrite(int ep, const Barry::Data &data, int timeout)
00286 {
00287         ddout("InterruptWrite to endpoint " << std::dec << ep << ":\n" << data);
00288 
00289         int ret;
00290         do {
00291                 ret = usb_interrupt_write(m_handle, ep,
00292                         (char*) data.GetData(), data.GetSize(),
00293                         timeout == -1 ? m_timeout : timeout);
00294                 if( ret < 0 && ret != -EINTR && ret != -EAGAIN ) {
00295                         m_lasterror = ret;
00296                         if( ret == -ETIMEDOUT )
00297                                 throw Timeout(ret, "Timeout in usb_bulk_read");
00298                         else
00299                                 throw Error(ret, "Error in usb_bulk_read");
00300                 }
00301         } while( ret == -EINTR || ret == -EAGAIN );
00302 
00303         return ret >= 0;
00304 }
00305 
00306 //
00307 // BulkDrain
00308 //
00309 /// Reads anything available on the given endpoint, with a low timeout,
00310 /// in order to clear any pending reads.
00311 ///
00312 void Device::BulkDrain(int ep, int timeout)
00313 {
00314         try {
00315                 Barry::Data data;
00316                 while( BulkRead(ep, data, timeout) )
00317                 ;
00318         }
00319         catch( Usb::Error & ) {}
00320 }
00321 
00322 //
00323 // GetConfiguration
00324 //
00325 /// Uses the GET_CONFIGURATION control message to determine the currently
00326 /// selected USB configuration, returning it in the cfg argument.
00327 /// If unsuccessful, returns false.
00328 ///
00329 bool Device::GetConfiguration(unsigned char &cfg)
00330 {
00331         int result = usb_control_msg(m_handle, 0x80, USB_REQ_GET_CONFIGURATION, 0, 0,
00332                 (char*) &cfg, 1, m_timeout);
00333         m_lasterror = result;
00334         return result >= 0;
00335 }
00336 
00337 
00338 
00339 ///////////////////////////////////////////////////////////////////////////////
00340 // Interface
00341 
00342 Interface::Interface(Device &dev, int iface)
00343         : m_dev(dev), m_iface(iface)
00344 {
00345         dout("usb_claim_interface(" << dev.GetHandle() << "," << std::dec << iface << ")");
00346         int ret = usb_claim_interface(dev.GetHandle(), iface);
00347         if( ret < 0 )
00348                 throw Error(ret, "claim interface failed");
00349 }
00350 
00351 Interface::~Interface()
00352 {
00353         dout("usb_release_interface(" << m_dev.GetHandle() << "," << std::dec << m_iface << ")");
00354         usb_release_interface(m_dev.GetHandle(), m_iface);
00355 }
00356 
00357 
00358 
00359 ///////////////////////////////////////////////////////////////////////////////
00360 // EndpointDiscovery
00361 
00362 bool EndpointDiscovery::Discover(struct usb_interface_descriptor *interface, int epcount)
00363 {
00364         // start fresh
00365         clear();
00366         m_valid = false;
00367 
00368         EndpointPair pair;
00369 
00370         if( !interface || !interface->endpoint ) {
00371                 dout("EndpointDiscovery::Discover: empty interface pointer");
00372                 return false;
00373         }
00374 
00375         for( int i = 0; i < epcount; i++ ) {
00376                 // load descriptor
00377                 usb_endpoint_descriptor desc;
00378                 desc = interface->endpoint[i];
00379                 dout("      endpoint_desc #" << i << " loaded"
00380                         << "\nbLength: " << (unsigned ) desc.bLength
00381                         << "\nbDescriptorType: " << (unsigned ) desc.bDescriptorType
00382                         << "\nbEndpointAddress: " << (unsigned ) desc.bEndpointAddress
00383                         << "\nbmAttributes: " << (unsigned ) desc.bmAttributes
00384                         << "\nwMaxPacketSize: " << (unsigned ) desc.wMaxPacketSize
00385                         << "\nbInterval: " << (unsigned ) desc.bInterval
00386                         << "\nbRefresh: " << (unsigned ) desc.bRefresh
00387                         << "\nbSynchAddress: " << (unsigned ) desc.bSynchAddress
00388                         << "\n"
00389                         );
00390 
00391                 // add to the map
00392                 (*this)[desc.bEndpointAddress] = desc;
00393                 dout("      endpoint added to map with bEndpointAddress: " << (unsigned int)desc.bEndpointAddress);
00394 
00395                 // parse the endpoint into read/write sets, if possible,
00396                 // going in discovery order...
00397                 // Assumptions:
00398                 //      - endpoints of related utility will be grouped
00399                 //      - endpoints with same type will be grouped
00400                 //      - endpoints that do not meet the above assumptions
00401                 //              do not belong in a pair
00402                 unsigned char type = desc.bmAttributes & USB_ENDPOINT_TYPE_MASK;
00403                 if( desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK ) {
00404                         // read endpoint
00405                         pair.read = desc.bEndpointAddress;
00406                         dout("        pair.read = " << (unsigned int)pair.read);
00407                         if( pair.IsTypeSet() && pair.type != type ) {
00408                                 // if type is already set, we must start over
00409                                 pair.write = 0;
00410                         }
00411                 }
00412                 else {
00413                         // write endpoint
00414                         pair.write = desc.bEndpointAddress;
00415                         dout("        pair.write = " << (unsigned int)pair.write);
00416                         if( pair.IsTypeSet() && pair.type != type ) {
00417                                 // if type is already set, we must start over
00418                                 pair.read = 0;
00419                         }
00420                 }
00421                 // save the type last
00422                 pair.type = type;
00423                 dout("        pair.type = " << (unsigned int)pair.type);
00424 
00425                 // if pair is complete, add to array
00426                 if( pair.IsComplete() ) {
00427                         m_endpoints.push_back(pair);
00428                         dout("        pair added! ("
00429                                 << "read: " << (unsigned int)pair.read << ","
00430                                 << "write: " << (unsigned int)pair.write << ","
00431                                 << "type: " << (unsigned int)pair.type << ")");
00432                         pair = EndpointPair();  // clear
00433                 }
00434         }
00435 
00436         // just for debugging purposes, check for extra descriptors, and
00437         // dump them to dout if they exist
00438         if( interface->extra ) {
00439                 dout("while parsing endpoints, found a block of extra descriptors:");
00440                 Barry::Data data(interface->extra, interface->extralen);
00441                 dout(data);
00442         }
00443 
00444         return m_valid = true;
00445 }
00446 
00447 
00448 ///////////////////////////////////////////////////////////////////////////////
00449 // InterfaceDiscovery
00450 
00451 bool InterfaceDiscovery::DiscoverInterface(struct usb_interface *interface)
00452 {
00453         if( !interface->altsetting ) {
00454                 dout("InterfaceDiscovery::DiscoverIterface: empty altsetting");
00455                 // some devices are buggy and return a higher bNumInterfaces
00456                 // than the number of interfaces available... in this case
00457                 // we just skip and continue
00458                 return true;
00459         }
00460 
00461         for( int i = 0; i < interface->num_altsetting; i++ ) {
00462                 // load descriptor
00463                 InterfaceDesc desc;
00464                 desc.desc = interface->altsetting[i];
00465                 dout("    interface_desc #" << i << " loaded"
00466                         << "\nbLength: " << (unsigned) desc.desc.bLength
00467                         << "\nbDescriptorType: " << (unsigned) desc.desc.bDescriptorType
00468                         << "\nbInterfaceNumber: " << (unsigned) desc.desc.bInterfaceNumber
00469                         << "\nbAlternateSetting: " << (unsigned) desc.desc.bAlternateSetting
00470                         << "\nbNumEndpoints: " << (unsigned) desc.desc.bNumEndpoints
00471                         << "\nbInterfaceClass: " << (unsigned) desc.desc.bInterfaceClass
00472                         << "\nbInterfaceSubClass: " << (unsigned) desc.desc.bInterfaceSubClass
00473                         << "\nbInterfaceProtocol: " << (unsigned) desc.desc.bInterfaceProtocol
00474                         << "\niInterface: " << (unsigned) desc.desc.iInterface
00475                         << "\n"
00476                         );
00477 
00478                 // load all endpoints on this interface
00479                 if( !desc.endpoints.Discover(&desc.desc, desc.desc.bNumEndpoints) ) {
00480                         dout("    endpoint discovery failed for bInterfaceNumber: " << (unsigned int)desc.desc.bInterfaceNumber << ", not added to map.");
00481                         return false;
00482                 }
00483 
00484                 // add to the map
00485                 (*this)[desc.desc.bInterfaceNumber] = desc;
00486                 dout("    interface added to map with bInterfaceNumber: " << (unsigned int)desc.desc.bInterfaceNumber);
00487         }
00488         return true;
00489 }
00490 
00491 bool InterfaceDiscovery::Discover(Usb::DeviceIDType devid, int cfgidx, int ifcount)
00492 {
00493         // start fresh
00494         clear();
00495         m_valid = false;
00496 
00497         if( !devid || !devid->config || !devid->config[cfgidx].interface ) {
00498                 dout("InterfaceDiscovery::Discover: empty devid/config/interface");
00499                 return false;
00500         }
00501 
00502         for( int i = 0; i < ifcount; i++ ) {
00503                 if( !DiscoverInterface(&devid->config[cfgidx].interface[i]) )
00504                         return false;
00505         }
00506 
00507         return m_valid = true;
00508 }
00509 
00510 
00511 ///////////////////////////////////////////////////////////////////////////////
00512 // ConfigDiscovery
00513 
00514 bool ConfigDiscovery::Discover(Usb::DeviceIDType devid, int cfgcount)
00515 {
00516         // start fresh
00517         clear();
00518         m_valid = false;
00519 
00520         for( int i = 0; i < cfgcount; i++ ) {
00521                 // load descriptor
00522                 ConfigDesc desc;
00523                 if( !devid || !devid->config ) {
00524                         dout("ConfigDiscovery::Discover: empty devid or config");
00525                         return false;
00526                 }
00527                 desc.desc = devid->config[i];
00528                 dout("  config_desc #" << i << " loaded"
00529                         << "\nbLength: " << (unsigned int) desc.desc.bLength
00530                         << "\nbDescriptorType: " << (unsigned int) desc.desc.bDescriptorType
00531                         << "\nwTotalLength: " << (unsigned int) desc.desc.wTotalLength
00532                         << "\nbNumInterfaces: " << (unsigned int) desc.desc.bNumInterfaces
00533                         << "\nbConfigurationValue: " << (unsigned int) desc.desc.bConfigurationValue
00534                         << "\niConfiguration: " << (unsigned int) desc.desc.iConfiguration
00535                         << "\nbmAttributes: " << (unsigned int) desc.desc.bmAttributes
00536                         << "\nMaxPower: " << (unsigned int) desc.desc.MaxPower
00537                         << "\n"
00538                         );
00539 
00540                 // just for debugging purposes, check for extra descriptors, and
00541                 // dump them to dout if they exist
00542                 if( desc.desc.extra ) {
00543                         dout("while parsing config descriptor, found a block of extra descriptors:");
00544                         Barry::Data data(desc.desc.extra, desc.desc.extralen);
00545                         dout(data);
00546                 }
00547 
00548                 // load all interfaces on this configuration
00549                 if( !desc.interfaces.Discover(devid, i, desc.desc.bNumInterfaces) ) {
00550                         dout("  config discovery failed for bConfigurationValue: " << (unsigned int)desc.desc.bConfigurationValue << ", not added to map.");
00551                         return false;
00552                 }
00553 
00554                 // add to the map
00555                 (*this)[desc.desc.bConfigurationValue] = desc;
00556                 dout("  config added to map with bConfigurationValue: " << (unsigned int)desc.desc.bConfigurationValue);
00557         }
00558 
00559         return m_valid = true;
00560 }
00561 
00562 
00563 ///////////////////////////////////////////////////////////////////////////////
00564 // DeviceDiscovery
00565 
00566 DeviceDiscovery::DeviceDiscovery(Usb::DeviceIDType devid)
00567         : m_valid(false)
00568 {
00569         Discover(devid);
00570 }
00571 
00572 bool DeviceDiscovery::Discover(Usb::DeviceIDType devid)
00573 {
00574         // start fresh
00575         configs.clear();
00576         m_valid = false;
00577 
00578         // copy the descriptor over to our memory
00579         if( !devid ) {
00580                 dout("DeviceDiscovery::Discover: empty devid");
00581                 return false;
00582         }
00583 
00584         desc = devid->descriptor;
00585         dout("device_desc loaded"
00586                 << "\nbLength: " << (unsigned int) desc.bLength
00587                 << "\nbDescriptorType: " << (unsigned int) desc.bDescriptorType
00588                 << "\nbcdUSB: " << (unsigned int) desc.bcdUSB
00589                 << "\nbDeviceClass: " << (unsigned int) desc.bDeviceClass
00590                 << "\nbDeviceSubClass: " << (unsigned int) desc.bDeviceSubClass
00591                 << "\nbDeviceProtocol: " << (unsigned int) desc.bDeviceProtocol
00592                 << "\nbMaxPacketSize0: " << (unsigned int) desc.bMaxPacketSize0
00593                 << "\nidVendor: " << (unsigned int) desc.idVendor
00594                 << "\nidProduct: " << (unsigned int) desc.idProduct
00595                 << "\nbcdDevice: " << (unsigned int) desc.bcdDevice
00596                 << "\niManufacturer: " << (unsigned int) desc.iManufacturer
00597                 << "\niProduct: " << (unsigned int) desc.iProduct
00598                 << "\niSerialNumber: " << (unsigned int) desc.iSerialNumber
00599                 << "\nbNumConfigurations: " << (unsigned int) desc.bNumConfigurations
00600                 << "\n"
00601         );
00602 
00603         m_valid = configs.Discover(devid, desc.bNumConfigurations);
00604         return m_valid;
00605 }
00606 
00607 } // namespace Usb
00608 

Generated on Mon Jan 12 10:51:14 2009 for Barry by  doxygen 1.5.7.1