Removed usb_control message check from IpModem endpoint probing.
[barry/pauldeden.git] / src / probe.cc
blob2a6d5ae0df6675f39c08879826fb5094fd0ece88
1 ///
2 /// \file probe.cc
3 /// USB Blackberry detection routines
4 ///
6 /*
7 Copyright (C) 2005-2008, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "common.h"
23 #include "probe.h"
24 #include "usbwrap.h"
25 #include "data.h"
26 #include "endian.h"
27 #include "error.h"
28 #include "debug.h"
29 #include "packet.h"
30 #include "socket.h"
31 #include "protocol.h"
32 #include "record-internal.h"
33 #include "strnlen.h"
34 #include <iomanip>
35 #include <errno.h>
36 #include <string.h>
38 using namespace Usb;
40 namespace Barry {
42 unsigned char Intro_Sends[][32] = {
43 // packet #1
44 { 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0x00, 0x00,
45 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
49 unsigned char Intro_Receives[][32] = {
50 // response to packet #1
51 { 0x00, 0x00, 0x10, 0x00, 0x02, 0xff, 0x00, 0x00,
52 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
55 namespace {
57 unsigned int GetSize(const unsigned char *packet)
59 uint16_t size = *((uint16_t *)&packet[2]);
60 return btohs(size);
63 bool Intro(int IntroIndex, const EndpointPair &ep, Device &dev, Data &response)
65 dev.BulkWrite(ep.write, Intro_Sends[IntroIndex],
66 GetSize(Intro_Sends[IntroIndex]));
67 try {
68 dev.BulkRead(ep.read, response, 500);
70 catch( Usb::Timeout &to ) {
71 ddout("BulkRead: " << to.what());
72 return false;
74 ddout("BulkRead (" << (unsigned int)ep.read << "):\n" << response);
75 return true;
78 } // anonymous namespace
81 bool Probe::CheckSize(const Data &data, unsigned int required)
83 const unsigned char *pd = data.GetData();
85 if( GetSize(pd) != (unsigned int) data.GetSize() ||
86 data.GetSize() < required ||
87 pd[4] != SB_COMMAND_FETCHED_ATTRIBUTE )
89 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd)
90 << ", data.GetSize(): " << data.GetSize()
91 << ", pd[4]: " << (unsigned int) pd[4]);
92 return false;
95 return true;
98 bool Probe::ParsePIN(const Data &data, uint32_t &pin)
100 // validate response data
101 const unsigned char *pd = data.GetData();
103 if( !CheckSize(data, 0x14) )
104 return false;
106 // capture the PIN
107 pin = btohl(*((uint32_t *) &pd[16]));
109 return true;
112 bool Probe::ParseDesc(const Data &data, std::string &desc)
114 if( !CheckSize(data, 29) )
115 return false;
117 // capture the description
118 const char *d = (const char*) &data.GetData()[28];
119 int maxlen = data.GetSize() - 28;
120 desc.assign(d, strnlen(d, maxlen));
122 return true;
125 Probe::Probe(const char *busname, const char *devname)
126 : m_fail_count(0)
128 // let the programmer pass in "" as well as 0
129 if( busname && !strlen(busname) )
130 busname = 0;
131 if( devname && !strlen(devname) )
132 devname = 0;
134 // Search for standard product ID first
135 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_BLACKBERRY, busname, devname);
137 // Search for Pearl devices second
139 // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
140 // the USB class 255 interface we need, but only the
141 // Mass Storage one. Here we search for PRODUCT_RIM_PEARL_DUAL,
142 // (ID 4) which has both enabled.
143 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_DUAL, busname, devname);
144 // And a special case, which behaves similar to the PEARL_DUAL,
145 // but with a unique Product ID.
146 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_8120, busname, devname);
149 void Probe::ProbeMatching(int vendor, int product,
150 const char *busname, const char *devname)
152 Usb::DeviceIDType devid;
154 Match match(vendor, product, busname, devname);
155 while( match.next_device(&devid) ) try {
156 ProbeDevice(devid);
158 catch( Usb::Error &e ) {
159 dout("Usb::Error exception caught: " << e.what());
160 if( e.libusb_errcode() == -EBUSY ) {
161 m_fail_count++;
162 m_fail_msgs.push_back(e.what());
164 else {
165 throw;
170 void Probe::ProbeDevice(Usb::DeviceIDType devid)
172 // skip if we can't properly discover device config
173 DeviceDiscovery discover(devid);
174 ConfigDesc &config = discover.configs[BLACKBERRY_CONFIGURATION];
176 // search for interface class
177 InterfaceDiscovery::base_type::iterator idi = config.interfaces.begin();
178 for( ; idi != config.interfaces.end(); idi++ ) {
179 if( idi->second.desc.bInterfaceClass == BLACKBERRY_DB_CLASS )
180 break;
182 if( idi == config.interfaces.end() ) {
183 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
184 << BLACKBERRY_DB_CLASS << ") not found.");
185 return; // not found
188 unsigned char InterfaceNumber = idi->second.desc.bInterfaceNumber;
189 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber);
191 // check endpoint validity
192 EndpointDiscovery &ed = config.interfaces[InterfaceNumber].endpoints;
193 if( !ed.IsValid() || ed.GetEndpointPairs().size() == 0 ) {
194 dout("Probe: endpoint invalid. ed.IsValud() == "
195 << (ed.IsValid() ? "true" : "false")
196 << ", ed.GetEndpointPairs().size() == "
197 << ed.GetEndpointPairs().size());
198 return;
201 ProbeResult result;
202 result.m_dev = devid;
203 result.m_interface = InterfaceNumber;
204 result.m_zeroSocketSequence = 0;
206 // open device
207 Device dev(devid);
208 // dev.Reset();
209 // sleep(5);
211 // make sure we're talking to the right config
212 unsigned char cfg;
213 if( !dev.GetConfiguration(cfg) )
214 throw Usb::Error(dev.GetLastError(),
215 "Probe: GetConfiguration failed");
216 if( cfg != BLACKBERRY_CONFIGURATION ) {
217 if( !dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
218 throw Usb::Error(dev.GetLastError(),
219 "Probe: SetConfiguration failed");
222 // open interface
223 Interface iface(dev, InterfaceNumber);
225 // find the first bulk read/write endpoint pair that answers
226 // to our probe commands
227 // Start with second pair, since evidence indicates the later pairs
228 // are the ones we need.
229 size_t i;
230 for(i = ed.GetEndpointPairs().size() > 1 ? 1 : 0;
231 i < ed.GetEndpointPairs().size();
232 i++ )
234 const EndpointPair &ep = ed.GetEndpointPairs()[i];
235 if( ep.type == USB_ENDPOINT_TYPE_BULK ) {
237 uint32_t pin;
238 uint8_t zeroSocketSequence;
239 std::string desc;
240 if( ProbePair(dev, ep, pin, desc, zeroSocketSequence) ) {
241 result.m_ep = ep;
242 result.m_pin = pin;
243 result.m_description = desc;
244 result.m_zeroSocketSequence = zeroSocketSequence;
245 break;
248 else {
249 dout("Probe: Skipping non-bulk endpoint pair (offset: "
250 << i-1 << ") ");
254 // check for ip modem endpoints
255 i++;
256 if( i < ed.GetEndpointPairs().size() ) {
257 const EndpointPair &ep = ed.GetEndpointPairs()[i];
258 if( ProbeModem(dev, ep) ) {
259 result.m_epModem = ep;
263 // add to list
264 if( result.m_ep.IsComplete() ) {
265 m_results.push_back(result);
266 ddout("Using ReadEndpoint: " << (unsigned int)result.m_ep.read);
267 ddout(" WriteEndpoint: " << (unsigned int)result.m_ep.write);
269 else {
270 ddout("Unable to discover endpoint pair for one device.");
274 bool Probe::ProbePair(Usb::Device &dev,
275 const Usb::EndpointPair &ep,
276 uint32_t &pin,
277 std::string &desc,
278 uint8_t &zeroSocketSequence)
280 dev.ClearHalt(ep.read);
281 dev.ClearHalt(ep.write);
283 Data data;
284 dev.BulkDrain(ep.read);
285 if( !Intro(0, ep, dev, data) ) {
286 dout("Probe: Intro(0) failed");
287 return false;
290 SocketZero socket(dev, ep.write, ep.read);
292 Data send, receive;
293 ZeroPacket packet(send, receive);
295 // unknown attribute: 0x14 / 0x01
296 packet.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN,
297 SB_ATTR_INITIAL_UNKNOWN);
298 socket.Send(packet);
300 // fetch PIN
301 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_PIN);
302 socket.Send(packet);
303 if( packet.ObjectID() != SB_OBJECT_PROFILE ||
304 packet.AttributeID() != SB_ATTR_PROFILE_PIN ||
305 !ParsePIN(receive, pin) )
307 dout("Probe: unable to fetch PIN");
308 return false;
311 // fetch Description
312 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_DESC);
313 socket.Send(packet);
314 // response ObjectID does not match request... :-/
315 if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
316 packet.AttributeID() != SB_ATTR_PROFILE_DESC ||
317 !ParseDesc(receive, desc) )
319 dout("Probe: unable to fetch description");
322 // more unknowns:
323 for( uint16_t attr = 5; attr < 9; attr++ ) {
324 packet.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN, attr);
325 socket.Send(packet);
326 // FIXME parse these responses, if they turn
327 // out to be important
330 // all info obtained!
331 zeroSocketSequence = socket.GetZeroSocketSequence();
332 return true;
335 bool Probe::ProbeModem(Usb::Device &dev, const Usb::EndpointPair &ep)
338 // This check is not needed for all devices. Some devices,
339 // like the 8700 have both the RIM_UsbSerData mode and IpModem mode.
341 // If this function is called, then we have extra endpoints,
342 // so might as well try them.
344 // FIXME - someday, we might wish to confirm that the endpoints
345 // work as a modem, and return true/false based on that test.
347 return true;
350 // Thanks to Rick Scott (XmBlackBerry:bb_usb.c) for reverse engineering this
351 // int num_read;
352 // char data[255];
353 // int local_errno;
355 // num_read = usb_control_msg(dev.GetHandle(),
356 // /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
357 // /* bRequest */ 0xa5,
358 // /* wValue */ 0,
359 // /* wIndex */ 1,
360 // /* data */ data,
361 // /* wLength */ sizeof(data),
362 // /* timeout */ 2000);
363 // local_errno = errno;
364 // if( num_read > 1 ) {
365 // if( data[0] == 0x02 ) {
366 // return true;
367 // }
368 // }
369 // return false;
372 int Probe::FindActive(uint32_t pin) const
374 for( int i = 0; i < GetCount(); i++ ) {
375 if( Get(i).m_pin == pin )
376 return i;
378 if( pin == 0 ) {
379 // can we default to a single device?
380 if( GetCount() == 1 )
381 return 0; // yes!
384 // PIN not found
385 return -1;
388 void ProbeResult::DumpAll(std::ostream &os) const
390 os << *this
391 << ", Interface: 0x" << std::hex << (unsigned int) m_interface
392 << ", Endpoints: (read: 0x" << std::hex << (unsigned int) m_ep.read
393 << ", write: 0x" << std::hex << (unsigned int) m_ep.write
394 << ", type: 0x" << std::hex << (unsigned int) m_ep.type
395 << ", ZeroSocketSequence: 0x" << std::hex << (unsigned int) m_zeroSocketSequence;
398 std::ostream& operator<< (std::ostream &os, const ProbeResult &pr)
400 os << "Device ID: " << pr.m_dev
401 << std::hex << ". PIN: " << pr.m_pin
402 << ", Description: " << pr.m_description;
403 return os;
406 } // namespace Barry