Added 'addmemo' example, to test newline behaviour
[barry.git] / src / probe.cc
blob56add873cb2af5312eeb1c87fdf96fb901638e37
1 ///
2 /// \file probe.cc
3 /// USB Blackberry detection routines
4 ///
6 /*
7 Copyright (C) 2005-2009, 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 "configfile.h"
35 #include <iomanip>
36 #include <errno.h>
37 #include <string.h>
39 using namespace Usb;
41 namespace Barry {
43 unsigned char Intro_Sends[][32] = {
44 // packet #1
45 { 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0x00, 0x00,
46 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
50 unsigned char Intro_Receives[][32] = {
51 // response to packet #1
52 { 0x00, 0x00, 0x10, 0x00, 0x02, 0xff, 0x00, 0x00,
53 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
56 namespace {
58 unsigned int GetSize(const unsigned char *packet)
60 uint16_t size = *((uint16_t *)&packet[2]);
61 return btohs(size);
64 bool Intro(int IntroIndex, const EndpointPair &ep, Device &dev, Data &response)
66 dev.BulkWrite(ep.write, Intro_Sends[IntroIndex],
67 GetSize(Intro_Sends[IntroIndex]));
68 try {
69 dev.BulkRead(ep.read, response, 500);
71 catch( Usb::Timeout &to ) {
72 ddout("BulkRead: " << to.what());
73 return false;
75 ddout("BulkRead (" << (unsigned int)ep.read << "):\n" << response);
76 return true;
79 } // anonymous namespace
82 bool Probe::CheckSize(const Data &data, unsigned int required)
84 const unsigned char *pd = data.GetData();
86 if( GetSize(pd) != (unsigned int) data.GetSize() ||
87 data.GetSize() < required ||
88 pd[4] != SB_COMMAND_FETCHED_ATTRIBUTE )
90 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd)
91 << ", data.GetSize(): " << data.GetSize()
92 << ", pd[4]: " << (unsigned int) pd[4]);
93 return false;
96 return true;
99 bool Probe::ParsePIN(const Data &data, uint32_t &pin)
101 // validate response data
102 const unsigned char *pd = data.GetData();
104 if( !CheckSize(data, 0x14) )
105 return false;
107 // capture the PIN
108 pin = btohl(*((uint32_t *) &pd[16]));
110 return true;
113 bool Probe::ParseDesc(const Data &data, std::string &desc)
115 if( !CheckSize(data, 29) )
116 return false;
118 // capture the description
119 const char *d = (const char*) &data.GetData()[28];
120 int maxlen = data.GetSize() - 28;
121 desc.assign(d, strnlen(d, maxlen));
123 return true;
126 Probe::Probe(const char *busname, const char *devname,
127 const Usb::EndpointPair *epp)
128 : m_fail_count(0)
129 , m_epp_override(epp)
131 if( m_epp_override ) {
132 m_epp = *epp;
135 // let the programmer pass in "" as well as 0
136 if( busname && !strlen(busname) )
137 busname = 0;
138 if( devname && !strlen(devname) )
139 devname = 0;
141 // Search for standard product ID first
142 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_BLACKBERRY, busname, devname);
144 // Search for Pearl devices second
146 // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
147 // the USB class 255 interface we need, but only the
148 // Mass Storage one. Here we search for PRODUCT_RIM_PEARL_DUAL,
149 // (ID 4) which has both enabled.
150 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_DUAL, busname, devname);
151 // And a special case, which behaves similar to the PEARL_DUAL,
152 // but with a unique Product ID.
153 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_8120, busname, devname);
154 // And one more! The Pearl Flip
155 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_FLIP, busname, devname);
157 // And one more time, for the Blackberry Storm
158 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_STORM, busname, devname);
161 void Probe::ProbeMatching(int vendor, int product,
162 const char *busname, const char *devname)
164 Usb::DeviceIDType devid;
166 Match match(vendor, product, busname, devname);
167 while( match.next_device(&devid) ) try {
168 ProbeDevice(devid);
170 catch( Usb::Error &e ) {
171 dout("Usb::Error exception caught: " << e.what());
172 if( e.libusb_errcode() == -EBUSY ) {
173 m_fail_count++;
174 m_fail_msgs.push_back(e.what());
176 else {
177 throw;
182 void Probe::ProbeDevice(Usb::DeviceIDType devid)
184 // skip if we can't properly discover device config
185 DeviceDiscovery discover(devid);
186 ConfigDesc &config = discover.configs[BLACKBERRY_CONFIGURATION];
188 // search for interface class
189 InterfaceDiscovery::base_type::iterator idi = config.interfaces.begin();
190 for( ; idi != config.interfaces.end(); idi++ ) {
191 if( idi->second.desc.bInterfaceClass == BLACKBERRY_DB_CLASS )
192 break;
194 if( idi == config.interfaces.end() ) {
195 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
196 << BLACKBERRY_DB_CLASS << ") not found.");
197 return; // not found
200 unsigned char InterfaceNumber = idi->second.desc.bInterfaceNumber;
201 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber);
203 // check endpoint validity
204 EndpointDiscovery &ed = config.interfaces[InterfaceNumber].endpoints;
205 if( !ed.IsValid() || ed.GetEndpointPairs().size() == 0 ) {
206 dout("Probe: endpoint invalid. ed.IsValud() == "
207 << (ed.IsValid() ? "true" : "false")
208 << ", ed.GetEndpointPairs().size() == "
209 << ed.GetEndpointPairs().size());
210 return;
213 ProbeResult result;
214 result.m_dev = devid;
215 result.m_interface = InterfaceNumber;
216 result.m_zeroSocketSequence = 0;
218 // open device
219 Device dev(devid);
220 // dev.Reset();
221 // sleep(5);
223 // make sure we're talking to the right config
224 unsigned char cfg;
225 if( !dev.GetConfiguration(cfg) )
226 throw Usb::Error(dev.GetLastError(),
227 "Probe: GetConfiguration failed");
228 if( cfg != BLACKBERRY_CONFIGURATION ) {
229 if( !dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
230 throw Usb::Error(dev.GetLastError(),
231 "Probe: SetConfiguration failed");
234 // open interface
235 Interface iface(dev, InterfaceNumber);
237 if( m_epp_override ) {
238 // user has given us endpoints to try... so try them
239 uint32_t pin;
240 uint8_t zeroSocketSequence;
241 std::string desc;
242 if( ProbePair(dev, m_epp, pin, desc, zeroSocketSequence) ) {
243 // looks good, finish filling out the result
244 result.m_ep = m_epp;
245 result.m_pin = pin;
246 result.m_description = desc;
247 result.m_zeroSocketSequence = zeroSocketSequence;
250 else {
251 // find the first bulk read/write endpoint pair that answers
252 // to our probe commands
253 // Start with second pair, since evidence indicates the later pairs
254 // are the ones we need.
255 size_t i;
256 for(i = ed.GetEndpointPairs().size() > 1 ? 1 : 0;
257 i < ed.GetEndpointPairs().size();
258 i++ )
260 const EndpointPair &ep = ed.GetEndpointPairs()[i];
261 if( ep.type == USB_ENDPOINT_TYPE_BULK ) {
263 uint32_t pin;
264 uint8_t zeroSocketSequence;
265 std::string desc;
266 if( ProbePair(dev, ep, pin, desc, zeroSocketSequence) ) {
267 result.m_ep = ep;
268 result.m_pin = pin;
269 result.m_description = desc;
270 result.m_zeroSocketSequence = zeroSocketSequence;
271 break;
274 else {
275 dout("Probe: Skipping non-bulk endpoint pair (offset: "
276 << i-1 << ") ");
280 // check for ip modem endpoints
281 i++;
282 if( i < ed.GetEndpointPairs().size() ) {
283 const EndpointPair &ep = ed.GetEndpointPairs()[i];
284 if( ProbeModem(dev, ep) ) {
285 result.m_epModem = ep;
290 // add to list
291 if( result.m_ep.IsComplete() ) {
292 // before adding to list, try to load the device's
293 // friendly name from the configfile... but don't
294 // fail if we can't do it
295 try {
296 ConfigFile cfg(result.m_pin);
297 result.m_cfgDeviceName = cfg.GetDeviceName();
299 catch( Barry::ConfigFileError & ) {
300 // ignore...
303 m_results.push_back(result);
304 ddout("Using ReadEndpoint: " << (unsigned int)result.m_ep.read);
305 ddout(" WriteEndpoint: " << (unsigned int)result.m_ep.write);
307 else {
308 ddout("Unable to discover endpoint pair for one device.");
312 bool Probe::ProbePair(Usb::Device &dev,
313 const Usb::EndpointPair &ep,
314 uint32_t &pin,
315 std::string &desc,
316 uint8_t &zeroSocketSequence)
318 dev.ClearHalt(ep.read);
319 dev.ClearHalt(ep.write);
321 Data data;
322 dev.BulkDrain(ep.read);
323 if( !Intro(0, ep, dev, data) ) {
324 dout("Probe: Intro(0) failed");
325 return false;
328 SocketZero socket(dev, ep.write, ep.read);
330 Data send, receive;
331 ZeroPacket packet(send, receive);
333 // unknown attribute: 0x14 / 0x01
334 packet.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN,
335 SB_ATTR_INITIAL_UNKNOWN);
336 socket.Send(packet);
338 // fetch PIN
339 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_PIN);
340 socket.Send(packet);
341 if( packet.ObjectID() != SB_OBJECT_PROFILE ||
342 packet.AttributeID() != SB_ATTR_PROFILE_PIN ||
343 !ParsePIN(receive, pin) )
345 dout("Probe: unable to fetch PIN");
346 return false;
349 // fetch Description
350 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_DESC);
351 socket.Send(packet);
352 // response ObjectID does not match request... :-/
353 if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
354 packet.AttributeID() != SB_ATTR_PROFILE_DESC ||
355 !ParseDesc(receive, desc) )
357 dout("Probe: unable to fetch description");
360 // more unknowns:
361 for( uint16_t attr = 5; attr < 9; attr++ ) {
362 packet.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN, attr);
363 socket.Send(packet);
364 // FIXME parse these responses, if they turn
365 // out to be important
368 // all info obtained!
369 zeroSocketSequence = socket.GetZeroSocketSequence();
370 return true;
373 bool Probe::ProbeModem(Usb::Device &dev, const Usb::EndpointPair &ep)
376 // This check is not needed for all devices. Some devices,
377 // like the 8700 have both the RIM_UsbSerData mode and IpModem mode.
379 // If this function is called, then we have extra endpoints,
380 // so might as well try them.
382 // FIXME - someday, we might wish to confirm that the endpoints
383 // work as a modem, and return true/false based on that test.
385 return true;
388 // Thanks to Rick Scott (XmBlackBerry:bb_usb.c) for reverse engineering this
389 // int num_read;
390 // char data[255];
391 // int local_errno;
393 // num_read = usb_control_msg(dev.GetHandle(),
394 // /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
395 // /* bRequest */ 0xa5,
396 // /* wValue */ 0,
397 // /* wIndex */ 1,
398 // /* data */ data,
399 // /* wLength */ sizeof(data),
400 // /* timeout */ 2000);
401 // local_errno = errno;
402 // if( num_read > 1 ) {
403 // if( data[0] == 0x02 ) {
404 // return true;
405 // }
406 // }
407 // return false;
410 int Probe::FindActive(Barry::Pin pin) const
412 return FindActive(m_results, pin);
415 int Probe::FindActive(const Barry::Probe::Results &results, Barry::Pin pin)
417 Barry::Probe::Results::const_iterator ci = results.begin();
418 for( int i = 0; ci != results.end(); i++, ++ci ) {
419 if( ci->m_pin == pin )
420 return i;
422 if( pin == 0 ) {
423 // can we default to a single device?
424 if( results.size() == 1 )
425 return 0; // yes!
428 // PIN not found
429 return -1;
432 void ProbeResult::DumpAll(std::ostream &os) const
434 os << *this
435 << ", Interface: 0x" << std::hex << (unsigned int) m_interface
436 << ", Endpoints: (read: 0x" << std::hex << (unsigned int) m_ep.read
437 << ", write: 0x" << std::hex << (unsigned int) m_ep.write
438 << ", type: 0x" << std::hex << (unsigned int) m_ep.type
439 << ", ZeroSocketSequence: 0x" << std::hex << (unsigned int) m_zeroSocketSequence;
442 std::ostream& operator<< (std::ostream &os, const ProbeResult &pr)
444 os << "Device ID: " << pr.m_dev
445 << ". PIN: " << pr.m_pin.str()
446 << ", Description: " << pr.m_description;
447 if( pr.m_cfgDeviceName.size() )
448 os << ", Name: " << pr.m_cfgDeviceName;
449 return os;
452 } // namespace Barry