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