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