debian: bumped opensync 0.4x plugin to compat 6 (with similar fixes)
[barry/progweb.git] / src / probe.cc
blob4a278fc5313caa7112fee38dd9af9e2394643b85
1 ///
2 /// \file probe.cc
3 /// USB Blackberry detection routines
4 ///
6 /*
7 Copyright (C) 2005-2011, 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 "protostructs.h"
33 #include "record-internal.h"
34 #include "strnlen.h"
35 #include "configfile.h"
36 #include "platform.h"
37 #include <iomanip>
38 #include <errno.h>
39 #include <string.h>
40 #include "ios_state.h"
42 using namespace Usb;
44 namespace Barry {
46 unsigned char Intro_Sends[][32] = {
47 // packet #1
48 { 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0x00, 0x00,
49 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
53 unsigned char Intro_Receives[][32] = {
54 // response to packet #1
55 { 0x00, 0x00, 0x10, 0x00, 0x02, 0xff, 0x00, 0x00,
56 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
59 namespace {
61 unsigned int GetSize(const unsigned char *packet)
63 const Protocol::Packet *pack = (const Protocol::Packet*) packet;
64 return btohs(pack->size);
67 bool Intro(int IntroIndex, const EndpointPair &ep, Device &dev, Data &response)
69 dev.BulkWrite(ep.write, Intro_Sends[IntroIndex],
70 GetSize(Intro_Sends[IntroIndex]));
71 try {
72 dev.BulkRead(ep.read, response, 500);
74 catch( Usb::Timeout &to ) {
75 ddout("BulkRead: " << to.what());
76 return false;
78 ddout("BulkRead (" << (unsigned int)ep.read << "):\n" << response);
79 return true;
82 } // anonymous namespace
85 bool Probe::CheckSize(const Data &data, unsigned int required)
87 const unsigned char *pd = data.GetData();
89 if( GetSize(pd) != (unsigned int) data.GetSize() ||
90 data.GetSize() < required ||
91 pd[4] != SB_COMMAND_FETCHED_ATTRIBUTE )
93 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd)
94 << ", data.GetSize(): " << data.GetSize()
95 << ", pd[4]: " << (unsigned int) pd[4]);
96 return false;
99 return true;
102 bool Probe::ParsePIN(const Data &data, uint32_t &pin)
104 // validate response data
105 const unsigned char *pd = data.GetData();
107 if( !CheckSize(data, 0x14) )
108 return false;
110 // capture the PIN
111 memcpy(&pin, &pd[16], sizeof(pin));
112 pin = btohl(pin);
114 return true;
117 bool Probe::ParseDesc(const Data &data, std::string &desc)
119 if( !CheckSize(data, 29) )
120 return false;
122 // capture the description
123 const char *d = (const char*) &data.GetData()[28];
124 int maxlen = data.GetSize() - 28;
125 desc.assign(d, strnlen(d, maxlen));
127 return true;
130 Probe::Probe(const char *busname, const char *devname,
131 const Usb::EndpointPair *epp)
132 : m_fail_count(0)
133 , m_epp_override(epp)
135 if( m_epp_override ) {
136 m_epp = *epp;
139 // let the programmer pass in "" as well as 0
140 if( busname && !strlen(busname) )
141 busname = 0;
142 if( devname && !strlen(devname) )
143 devname = 0;
145 // Search for standard product ID first
146 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_BLACKBERRY, busname, devname);
148 // Search for Pearl devices second
150 // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
151 // the USB class 255 interface we need, but only the
152 // Mass Storage one. Here we search for PRODUCT_RIM_PEARL_DUAL,
153 // (ID 4) which has both enabled.
154 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_DUAL, busname, devname);
155 // And a special case, which behaves similar to the PEARL_DUAL,
156 // but with a unique Product ID.
157 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_8120, busname, devname);
158 // And one more! The Pearl Flip
159 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_FLIP, busname, devname);
161 // And one more time, for the Blackberry Storm
162 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_STORM, busname, devname);
165 void Probe::ProbeMatching(int vendor, int product,
166 const char *busname, const char *devname)
168 Usb::DeviceID devid;
170 Match match(m_devices, vendor, product, busname, devname);
171 while( match.next_device(devid) ) try {
172 ProbeDevice(devid);
174 catch( Usb::Error &e ) {
175 dout("Usb::Error exception caught: " << e.what());
176 if( e.system_errcode() == -EBUSY ) {
177 m_fail_count++;
178 m_fail_msgs.push_back(e.what());
180 else {
181 throw;
186 void Probe::ProbeDevice(Usb::DeviceID& devid)
188 // skip if we can't properly discover device config
189 DeviceDescriptor desc(devid);
190 ConfigDescriptor* config = desc[BLACKBERRY_CONFIGURATION];
192 // search for interface class
193 ConfigDescriptor::base_type::iterator idi = config->begin();
194 for( ; idi != config->end(); idi++ ) {
195 if( idi->second->GetClass() == BLACKBERRY_DB_CLASS )
196 break;
198 if( idi == config->end() ) {
199 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
200 << BLACKBERRY_DB_CLASS << ") not found.");
201 return; // not found
204 unsigned char InterfaceNumber = idi->second->GetNumber();
205 unsigned char InterfaceAltSetting = idi->second->GetAltSetting();
206 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber <<
207 " AltSetting: " << (unsigned int) InterfaceAltSetting);
209 // check endpoint validity
210 EndpointPairings ep(*(*config)[InterfaceNumber]);
211 if( !ep.IsValid() || ep.size() == 0 ) {
212 dout("Probe: endpoint invalid. ep.IsValid() == "
213 << (ep.IsValid() ? "true" : "false")
214 << ", ep.size() == "
215 << ep.size());
216 return;
219 ProbeResult result;
220 result.m_dev = devid;
221 result.m_interface = InterfaceNumber;
222 result.m_altsetting = InterfaceAltSetting;
223 result.m_zeroSocketSequence = 0;
225 // open device
226 Device dev(devid);
227 // dev.Reset();
228 // sleep(5);
230 // make sure we're talking to the right config
231 unsigned char cfg;
232 if( !dev.GetConfiguration(cfg) )
233 throw Usb::Error(dev.GetLastError(),
234 "Probe: GetConfiguration failed");
235 if( cfg != BLACKBERRY_CONFIGURATION || MUST_SET_CONFIGURATION ) {
236 if( !dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
237 throw Usb::Error(dev.GetLastError(),
238 "Probe: SetConfiguration failed");
241 // open interface
242 Interface iface(dev, InterfaceNumber);
244 // Try the initial probing of endpoints
245 ProbeDeviceEndpoints(dev, ep, result);
247 if( !result.m_ep.IsComplete() ) {
248 // Probing of end-points failed, so try reprobing
249 // after calling usb_set_altinterface().
251 // Calling usb_set_altinterface() should be harmless
252 // and can help the host and device to synchronize the
253 // USB state, especially on FreeBSD and Mac OS X.
254 // However it can cause usb-storage URBs to be lost
255 // on some devices, so is only used if necessary.
256 dout("Probe: probing endpoints failed, retrying after setting alternate interface");
258 iface.SetAltInterface(InterfaceAltSetting);
259 result.m_needSetAltInterface = true;
260 ProbeDeviceEndpoints(dev, ep, result);
263 // add to list
264 if( result.m_ep.IsComplete() ) {
265 // before adding to list, try to load the device's
266 // friendly name from the configfile... but don't
267 // fail if we can't do it
268 try {
269 ConfigFile cfg(result.m_pin);
270 result.m_cfgDeviceName = cfg.GetDeviceName();
272 catch( Barry::ConfigFileError & ) {
273 // ignore...
276 m_results.push_back(result);
277 ddout("Using ReadEndpoint: " << (unsigned int)result.m_ep.read);
278 ddout(" WriteEndpoint: " << (unsigned int)result.m_ep.write);
280 else {
281 ddout("Unable to discover endpoint pair for one device.");
285 void Probe::ProbeDeviceEndpoints(Device &dev, EndpointPairings &ed, ProbeResult &result)
287 if( m_epp_override ) {
288 // user has given us endpoints to try... so try them
289 uint32_t pin;
290 uint8_t zeroSocketSequence;
291 std::string desc;
292 bool needClearHalt;
293 if( ProbePair(dev, m_epp, pin, desc, zeroSocketSequence, needClearHalt) ) {
294 // looks good, finish filling out the result
295 result.m_ep = m_epp;
296 result.m_pin = pin;
297 result.m_description = desc;
298 result.m_zeroSocketSequence = zeroSocketSequence;
299 result.m_needClearHalt = needClearHalt;
302 else {
303 // find the first bulk read/write endpoint pair that answers
304 // to our probe commands
305 // Start with second pair, since evidence indicates the later pairs
306 // are the ones we need.
307 size_t i;
308 for(i = ed.size() > 1 ? 1 : 0;
309 i < ed.size();
310 i++ )
312 const EndpointPair &ep = ed[i];
313 if( ep.type == Usb::EndpointDescriptor::BulkType ) {
315 uint32_t pin;
316 uint8_t zeroSocketSequence;
317 std::string desc;
318 bool needClearHalt;
319 if( ProbePair(dev, ep, pin, desc, zeroSocketSequence, needClearHalt) ) {
320 result.m_ep = ep;
321 result.m_pin = pin;
322 result.m_description = desc;
323 result.m_zeroSocketSequence = zeroSocketSequence;
324 result.m_needClearHalt = needClearHalt;
325 break;
328 else {
329 dout("Probe: Skipping non-bulk endpoint pair (offset: "
330 << i-1 << ") ");
334 // check for ip modem endpoints
335 i++;
336 if( i < ed.size() ) {
337 const EndpointPair &ep = ed[i];
338 if( ProbeModem(dev, ep) ) {
339 result.m_epModem = ep;
345 bool Probe::ProbePair(Usb::Device &dev,
346 const Usb::EndpointPair &ep,
347 uint32_t &pin,
348 std::string &desc,
349 uint8_t &zeroSocketSequence,
350 bool &needClearHalt)
352 // Initially assume that clear halt isn't needed as it causes some
353 // devices to drop packets. The suspicion is that the toggle bits
354 // get out of sync, but this hasn't been confirmed with hardware
355 // tracing.
357 // It is possible to always use clear halt, as long as SET
358 // INTERFACE has been sent before, via usb_set_altinterface().
359 // However this has the side affect that any outstanding URBs
360 // on other interfaces (i.e. usb-storage) timeout and lose
361 // their data. This is not a good thing as it can corrupt the
362 // file system exposed over usb-storage. This also has the
363 // side-affect that usb-storage issues a port reset after the
364 // 30 second timeout, which kills any current Barry
365 // connection.
367 // To further complicate matters some devices, such as the
368 // 8830, always need clear halt before they will respond to
369 // probes.
371 // So to work with all these device quirks the probe is first
372 // attempted without calling clear halt. If that probe fails
373 // then a clear halt is issued followed by a retry on the
374 // probing.
375 needClearHalt = false;
377 Data data;
378 dev.BulkDrain(ep.read);
379 if( !Intro(0, ep, dev, data) ) {
380 // Try clearing halt and then reprobing
381 dout("Probe: Intro(0) failed, retrying after clearing halt");
382 dev.ClearHalt(ep.read);
383 dev.ClearHalt(ep.write);
384 needClearHalt = true;
385 // Retry
386 dev.BulkDrain(ep.read);
387 if( !Intro(0, ep, dev, data) ) {
388 // Still no response so fail the probe
389 dout("Probe: Intro(0) still failed after clearing halt");
390 return false;
394 SocketZero socket(dev, ep.write, ep.read);
396 Data send, receive;
397 ZeroPacket packet(send, receive);
399 // unknown attribute: 0x14 / 0x01
400 packet.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN,
401 SB_ATTR_INITIAL_UNKNOWN);
402 socket.Send(packet);
404 // fetch PIN
405 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_PIN);
406 socket.Send(packet);
407 if( packet.ObjectID() != SB_OBJECT_PROFILE ||
408 packet.AttributeID() != SB_ATTR_PROFILE_PIN ||
409 !ParsePIN(receive, pin) )
411 dout("Probe: unable to fetch PIN");
412 return false;
415 // fetch Description
416 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_DESC);
417 socket.Send(packet);
418 // response ObjectID does not match request... :-/
419 if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
420 packet.AttributeID() != SB_ATTR_PROFILE_DESC ||
421 !ParseDesc(receive, desc) )
423 dout("Probe: unable to fetch description");
426 // more unknowns:
427 for( uint16_t attr = 5; attr < 9; attr++ ) {
428 packet.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN, attr);
429 socket.Send(packet);
430 // FIXME parse these responses, if they turn
431 // out to be important
434 // all info obtained!
435 zeroSocketSequence = socket.GetZeroSocketSequence();
436 return true;
439 bool Probe::ProbeModem(Usb::Device &dev, const Usb::EndpointPair &ep)
442 // This check is not needed for all devices. Some devices,
443 // like the 8700 have both the RIM_UsbSerData mode and IpModem mode.
445 // If this function is called, then we have extra endpoints,
446 // so might as well try them.
448 // FIXME - someday, we might wish to confirm that the endpoints
449 // work as a modem, and return true/false based on that test.
451 return true;
454 // Thanks to Rick Scott (XmBlackBerry:bb_usb.c) for reverse engineering this
455 // int num_read;
456 // char data[255];
457 // int local_errno;
459 // num_read = usb_control_msg(dev.GetHandle(),
460 // /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
461 // /* bRequest */ 0xa5,
462 // /* wValue */ 0,
463 // /* wIndex */ 1,
464 // /* data */ data,
465 // /* wLength */ sizeof(data),
466 // /* timeout */ 2000);
467 // local_errno = errno;
468 // if( num_read > 1 ) {
469 // if( data[0] == 0x02 ) {
470 // return true;
471 // }
472 // }
473 // return false;
476 int Probe::FindActive(Barry::Pin pin) const
478 return FindActive(m_results, pin);
481 int Probe::FindActive(const Barry::Probe::Results &results, Barry::Pin pin)
483 int i = Find(results, pin);
485 if( i == -1 && pin == 0 ) {
486 // can we default to a single device?
487 if( results.size() == 1 )
488 return 0; // yes!
491 return i;
494 int Probe::Find(const Results &results, Barry::Pin pin)
496 Barry::Probe::Results::const_iterator ci = results.begin();
497 for( int i = 0; ci != results.end(); i++, ++ci ) {
498 if( ci->m_pin == pin )
499 return i;
501 // PIN not found
502 return -1;
505 void ProbeResult::DumpAll(std::ostream &os) const
507 ios_format_state state(os);
509 os << *this
510 << ", Interface: 0x" << std::hex << (unsigned int) m_interface
511 << ", Endpoints: (read: 0x" << std::hex << (unsigned int) m_ep.read
512 << ", write: 0x" << std::hex << (unsigned int) m_ep.write
513 << ", type: 0x" << std::hex << (unsigned int) m_ep.type
514 << ", ZeroSocketSequence: 0x" << std::hex << (unsigned int) m_zeroSocketSequence;
517 std::ostream& operator<< (std::ostream &os, const ProbeResult &pr)
519 ios_format_state state(os);
521 os << "Device ID: " << pr.m_dev.m_impl.get()
522 << ". PIN: " << pr.m_pin.Str()
523 << ", Description: " << pr.m_description;
524 if( pr.m_cfgDeviceName.size() )
525 os << ", Name: " << pr.m_cfgDeviceName;
526 return os;
529 } // namespace Barry