probe.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       probe.cc
00003 ///             USB Blackberry detection routines
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2010, Net Direct Inc. (http://www.netdirect.ca/)
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 #include "common.h"
00023 #include "probe.h"
00024 #include "usbwrap.h"
00025 #include "data.h"
00026 #include "endian.h"
00027 #include "error.h"
00028 #include "debug.h"
00029 #include "packet.h"
00030 #include "socket.h"
00031 #include "protocol.h"
00032 #include "record-internal.h"
00033 #include "strnlen.h"
00034 #include "configfile.h"
00035 #include <iomanip>
00036 #include <errno.h>
00037 #include <string.h>
00038 
00039 using namespace Usb;
00040 
00041 namespace Barry {
00042 
00043 unsigned char Intro_Sends[][32] = {
00044         // packet #1
00045         { 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0x00, 0x00,
00046           0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
00047 };
00048 
00049 
00050 unsigned char Intro_Receives[][32] = {
00051         // response to packet #1
00052         { 0x00, 0x00, 0x10, 0x00, 0x02, 0xff, 0x00, 0x00,
00053           0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
00054 };
00055 
00056 namespace {
00057 
00058         unsigned int GetSize(const unsigned char *packet)
00059         {
00060                 uint16_t size = *((uint16_t *)&packet[2]);
00061                 return btohs(size);
00062         }
00063 
00064         bool Intro(int IntroIndex, const EndpointPair &ep, Device &dev, Data &response)
00065         {
00066                 dev.BulkWrite(ep.write, Intro_Sends[IntroIndex],
00067                         GetSize(Intro_Sends[IntroIndex]));
00068                 try {
00069                         dev.BulkRead(ep.read, response, 500);
00070                 }
00071                 catch( Usb::Timeout &to ) {
00072                         ddout("BulkRead: " << to.what());
00073                         return false;
00074                 }
00075                 ddout("BulkRead (" << (unsigned int)ep.read << "):\n" << response);
00076                 return true;
00077         }
00078 
00079 } // anonymous namespace
00080 
00081 
00082 bool Probe::CheckSize(const Data &data, unsigned int required)
00083 {
00084         const unsigned char *pd = data.GetData();
00085 
00086         if( GetSize(pd) != (unsigned int) data.GetSize() ||
00087             data.GetSize() < required ||
00088             pd[4] != SB_COMMAND_FETCHED_ATTRIBUTE )
00089         {
00090                 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd)
00091                         << ", data.GetSize(): " << data.GetSize()
00092                         << ", pd[4]: " << (unsigned int) pd[4]);
00093                 return false;
00094         }
00095 
00096         return true;
00097 }
00098 
00099 bool Probe::ParsePIN(const Data &data, uint32_t &pin)
00100 {
00101         // validate response data
00102         const unsigned char *pd = data.GetData();
00103 
00104         if( !CheckSize(data, 0x14) )
00105                 return false;
00106 
00107         // capture the PIN
00108         pin = btohl(*((uint32_t *) &pd[16]));
00109 
00110         return true;
00111 }
00112 
00113 bool Probe::ParseDesc(const Data &data, std::string &desc)
00114 {
00115         if( !CheckSize(data, 29) )
00116                 return false;
00117 
00118         // capture the description
00119         const char *d = (const char*) &data.GetData()[28];
00120         int maxlen = data.GetSize() - 28;
00121         desc.assign(d, strnlen(d, maxlen));
00122 
00123         return true;
00124 }
00125 
00126 Probe::Probe(const char *busname, const char *devname,
00127                 const Usb::EndpointPair *epp)
00128         : m_fail_count(0)
00129         , m_epp_override(epp)
00130 {
00131         if( m_epp_override ) {
00132                 m_epp = *epp;
00133         }
00134 
00135         // let the programmer pass in "" as well as 0
00136         if( busname && !strlen(busname) )
00137                 busname = 0;
00138         if( devname && !strlen(devname) )
00139                 devname = 0;
00140 
00141         // Search for standard product ID first
00142         ProbeMatching(VENDOR_RIM, PRODUCT_RIM_BLACKBERRY, busname, devname);
00143 
00144         // Search for Pearl devices second
00145         //
00146         // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
00147         // the USB class 255 interface we need, but only the
00148         // Mass Storage one.  Here we search for PRODUCT_RIM_PEARL_DUAL,
00149         // (ID 4) which has both enabled.
00150         ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_DUAL, busname, devname);
00151         // And a special case, which behaves similar to the PEARL_DUAL,
00152         // but with a unique Product ID.
00153         ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_8120, busname, devname);
00154         // And one more!  The Pearl Flip
00155         ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_FLIP, busname, devname);
00156 
00157         // And one more time, for the Blackberry Storm
00158         ProbeMatching(VENDOR_RIM, PRODUCT_RIM_STORM, busname, devname);
00159 }
00160 
00161 void Probe::ProbeMatching(int vendor, int product,
00162                         const char *busname, const char *devname)
00163 {
00164         Usb::DeviceIDType devid;
00165 
00166         Match match(vendor, product, busname, devname);
00167         while( match.next_device(&devid) ) try {
00168                 ProbeDevice(devid);
00169         }
00170         catch( Usb::Error &e ) {
00171                 dout("Usb::Error exception caught: " << e.what());
00172                 if( e.libusb_errcode() == -EBUSY ) {
00173                         m_fail_count++;
00174                         m_fail_msgs.push_back(e.what());
00175                 }
00176                 else {
00177                         throw;
00178                 }
00179         }
00180 }
00181 
00182 void Probe::ProbeDevice(Usb::DeviceIDType devid)
00183 {
00184         // skip if we can't properly discover device config
00185         DeviceDiscovery discover(devid);
00186         ConfigDesc &config = discover.configs[BLACKBERRY_CONFIGURATION];
00187 
00188         // search for interface class
00189         InterfaceDiscovery::base_type::iterator idi = config.interfaces.begin();
00190         for( ; idi != config.interfaces.end(); idi++ ) {
00191                 if( idi->second.desc.bInterfaceClass == BLACKBERRY_DB_CLASS )
00192                         break;
00193         }
00194         if( idi == config.interfaces.end() ) {
00195                 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
00196                         << BLACKBERRY_DB_CLASS << ") not found.");
00197                 return; // not found
00198         }
00199 
00200         unsigned char InterfaceNumber = idi->second.desc.bInterfaceNumber;
00201         dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber);
00202 
00203         // check endpoint validity
00204         EndpointDiscovery &ed = config.interfaces[InterfaceNumber].endpoints;
00205         if( !ed.IsValid() || ed.GetEndpointPairs().size() == 0 ) {
00206                 dout("Probe: endpoint invalid.   ed.IsValud() == "
00207                         << (ed.IsValid() ? "true" : "false")
00208                         << ", ed.GetEndpointPairs().size() == "
00209                         << ed.GetEndpointPairs().size());
00210                 return;
00211         }
00212 
00213         ProbeResult result;
00214         result.m_dev = devid;
00215         result.m_interface = InterfaceNumber;
00216         result.m_zeroSocketSequence = 0;
00217 
00218         // open device
00219         Device dev(devid);
00220 //      dev.Reset();
00221 //      sleep(5);
00222 
00223         //  make sure we're talking to the right config
00224         unsigned char cfg;
00225         if( !dev.GetConfiguration(cfg) )
00226                 throw Usb::Error(dev.GetLastError(),
00227                         "Probe: GetConfiguration failed");
00228         if( cfg != BLACKBERRY_CONFIGURATION ) {
00229                 if( !dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
00230                         throw Usb::Error(dev.GetLastError(),
00231                                 "Probe: SetConfiguration failed");
00232         }
00233 
00234         // open interface
00235         Interface iface(dev, InterfaceNumber);
00236 
00237         if( m_epp_override ) {
00238                 // user has given us endpoints to try... so try them
00239                 uint32_t pin;
00240                 uint8_t zeroSocketSequence;
00241                 std::string desc;
00242                 if( ProbePair(dev, m_epp, pin, desc, zeroSocketSequence) ) {
00243                         // looks good, finish filling out the result
00244                         result.m_ep = m_epp;
00245                         result.m_pin = pin;
00246                         result.m_description = desc;
00247                         result.m_zeroSocketSequence = zeroSocketSequence;
00248                 }
00249         }
00250         else {
00251                 // find the first bulk read/write endpoint pair that answers
00252                 // to our probe commands
00253                 // Start with second pair, since evidence indicates the later pairs
00254                 // are the ones we need.
00255                 size_t i;
00256                 for(i = ed.GetEndpointPairs().size() > 1 ? 1 : 0;
00257                     i < ed.GetEndpointPairs().size();
00258                     i++ )
00259                 {
00260                         const EndpointPair &ep = ed.GetEndpointPairs()[i];
00261                         if( ep.type == USB_ENDPOINT_TYPE_BULK ) {
00262 
00263                                 uint32_t pin;
00264                                 uint8_t zeroSocketSequence;
00265                                 std::string desc;
00266                                 if( ProbePair(dev, ep, pin, desc, zeroSocketSequence) ) {
00267                                         result.m_ep = ep;
00268                                         result.m_pin = pin;
00269                                         result.m_description = desc;
00270                                         result.m_zeroSocketSequence = zeroSocketSequence;
00271                                         break;
00272                                 }
00273                         }
00274                         else {
00275                                 dout("Probe: Skipping non-bulk endpoint pair (offset: "
00276                                         << i-1 << ") ");
00277                         }
00278                 }
00279 
00280                 // check for ip modem endpoints
00281                 i++;
00282                 if( i < ed.GetEndpointPairs().size() ) {
00283                         const EndpointPair &ep = ed.GetEndpointPairs()[i];
00284                         if( ProbeModem(dev, ep) ) {
00285                                 result.m_epModem = ep;
00286                         }
00287                 }
00288         }
00289 
00290         // add to list
00291         if( result.m_ep.IsComplete() ) {
00292                 // before adding to list, try to load the device's
00293                 // friendly name from the configfile... but don't
00294                 // fail if we can't do it
00295                 try {
00296                         ConfigFile cfg(result.m_pin);
00297                         result.m_cfgDeviceName = cfg.GetDeviceName();
00298                 }
00299                 catch( Barry::ConfigFileError & ) {
00300                         // ignore...
00301                 }
00302 
00303                 m_results.push_back(result);
00304                 ddout("Using ReadEndpoint: " << (unsigned int)result.m_ep.read);
00305                 ddout("      WriteEndpoint: " << (unsigned int)result.m_ep.write);
00306         }
00307         else {
00308                 ddout("Unable to discover endpoint pair for one device.");
00309         }
00310 }
00311 
00312 bool Probe::ProbePair(Usb::Device &dev,
00313                         const Usb::EndpointPair &ep,
00314                         uint32_t &pin,
00315                         std::string &desc,
00316                         uint8_t &zeroSocketSequence)
00317 {
00318         dev.ClearHalt(ep.read);
00319         dev.ClearHalt(ep.write);
00320 
00321         Data data;
00322         dev.BulkDrain(ep.read);
00323         if( !Intro(0, ep, dev, data) ) {
00324                 dout("Probe: Intro(0) failed");
00325                 return false;
00326         }
00327 
00328         SocketZero socket(dev, ep.write, ep.read);
00329 
00330         Data send, receive;
00331         ZeroPacket packet(send, receive);
00332 
00333         // unknown attribute: 0x14 / 0x01
00334         packet.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN,
00335                 SB_ATTR_INITIAL_UNKNOWN);
00336         socket.Send(packet);
00337 
00338         // fetch PIN
00339         packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_PIN);
00340         socket.Send(packet);
00341         if( packet.ObjectID() != SB_OBJECT_PROFILE ||
00342             packet.AttributeID() != SB_ATTR_PROFILE_PIN ||
00343             !ParsePIN(receive, pin) )
00344         {
00345                 dout("Probe: unable to fetch PIN");
00346                 return false;
00347         }
00348 
00349         // fetch Description
00350         packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_DESC);
00351         socket.Send(packet);
00352         // response ObjectID does not match request... :-/
00353         if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
00354             packet.AttributeID() != SB_ATTR_PROFILE_DESC ||
00355             !ParseDesc(receive, desc) )
00356         {
00357                 dout("Probe: unable to fetch description");
00358         }
00359 
00360         // more unknowns:
00361         for( uint16_t attr = 5; attr < 9; attr++ ) {
00362                 packet.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN, attr);
00363                 socket.Send(packet);
00364                 // FIXME parse these responses, if they turn
00365                 // out to be important
00366         }
00367 
00368         // all info obtained!
00369         zeroSocketSequence = socket.GetZeroSocketSequence();
00370         return true;
00371 }
00372 
00373 bool Probe::ProbeModem(Usb::Device &dev, const Usb::EndpointPair &ep)
00374 {
00375         //
00376         // This check is not needed for all devices.  Some devices,
00377         // like the 8700 have both the RIM_UsbSerData mode and IpModem mode.
00378         //
00379         // If this function is called, then we have extra endpoints,
00380         // so might as well try them.
00381         //
00382         // FIXME - someday, we might wish to confirm that the endpoints
00383         // work as a modem, and return true/false based on that test.
00384         //
00385         return true;
00386 
00387 
00388 // Thanks to Rick Scott (XmBlackBerry:bb_usb.c) for reverse engineering this
00389 //      int num_read;
00390 //      char data[255];
00391 //      int local_errno;
00392 //
00393 //      num_read = usb_control_msg(dev.GetHandle(),
00394 //              /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
00395 //              /* bRequest */ 0xa5,
00396 //              /* wValue */ 0,
00397 //              /* wIndex */ 1,
00398 //              /* data */ data,
00399 //              /* wLength */ sizeof(data),
00400 //              /* timeout */ 2000);
00401 //      local_errno = errno;
00402 //      if( num_read > 1 ) {
00403 //              if( data[0] == 0x02 ) {
00404 //                      return true;
00405 //              }
00406 //      }
00407 //      return false;
00408 }
00409 
00410 int Probe::FindActive(Barry::Pin pin) const
00411 {
00412         return FindActive(m_results, pin);
00413 }
00414 
00415 int Probe::FindActive(const Barry::Probe::Results &results, Barry::Pin pin)
00416 {
00417         int i = Find(results, pin);
00418 
00419         if( i == -1 && pin == 0 ) {
00420                 // can we default to a single device?
00421                 if( results.size() == 1 )
00422                         return 0;       // yes!
00423         }
00424 
00425         return i;
00426 }
00427 
00428 int Probe::Find(const Results &results, Barry::Pin pin)
00429 {
00430         Barry::Probe::Results::const_iterator ci = results.begin();
00431         for( int i = 0; ci != results.end(); i++, ++ci ) {
00432                 if( ci->m_pin == pin )
00433                         return i;
00434         }
00435         // PIN not found
00436         return -1;
00437 }
00438 
00439 void ProbeResult::DumpAll(std::ostream &os) const
00440 {
00441         os << *this
00442            << ", Interface: 0x" << std::hex << (unsigned int) m_interface
00443            << ", Endpoints: (read: 0x" << std::hex << (unsigned int) m_ep.read
00444                 << ", write: 0x" << std::hex << (unsigned int) m_ep.write
00445                 << ", type: 0x" << std::hex << (unsigned int) m_ep.type
00446            << ", ZeroSocketSequence: 0x" << std::hex << (unsigned int) m_zeroSocketSequence;
00447 }
00448 
00449 std::ostream& operator<< (std::ostream &os, const ProbeResult &pr)
00450 {
00451         os << "Device ID: " << pr.m_dev
00452            << ". PIN: " << pr.m_pin.str()
00453            << ", Description: " << pr.m_description;
00454         if( pr.m_cfgDeviceName.size() )
00455                 os << ", Name: " << pr.m_cfgDeviceName;
00456         return os;
00457 }
00458 
00459 } // namespace Barry
00460 

Generated on 29 Mar 2010 for Barry by  doxygen 1.6.1