tools: add playbook driver switch
[barry/progweb.git] / src / probe.cc
blobd4df4c4179c97e5df59ff47d87682944991212bf
1 ///
2 /// \file probe.cc
3 /// USB Blackberry detection routines
4 ///
6 /*
7 Copyright (C) 2005-2012, 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 <usb.h>
23 #include "common.h"
24 #include "probe.h"
25 #include "usbwrap.h"
26 #include "data.h"
27 #include "endian.h"
28 #include "error.h"
29 #include "debug.h"
30 #include "packet.h"
31 #include "socket.h"
32 #include "protocol.h"
33 #include "protostructs.h"
34 #include "record-internal.h"
35 #include "strnlen.h"
36 #include "configfile.h"
37 #include "platform.h"
38 #include <iomanip>
39 #include <sstream>
40 #include <errno.h>
41 #include <string.h>
42 #include "ios_state.h"
44 using namespace Usb;
46 namespace Barry {
48 unsigned char Intro_Sends[][32] = {
49 // packet #1
50 { 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0x00, 0x00,
51 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
55 unsigned char Intro_Receives[][32] = {
56 // response to packet #1
57 { 0x00, 0x00, 0x10, 0x00, 0x02, 0xff, 0x00, 0x00,
58 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
61 namespace {
63 unsigned int GetSize(const unsigned char *packet)
65 const Protocol::Packet *pack = (const Protocol::Packet*) packet;
66 return btohs(pack->size);
69 bool Intro(int IntroIndex, const EndpointPair &ep, Device &dev, Data &response)
71 dev.BulkWrite(ep.write, Intro_Sends[IntroIndex],
72 GetSize(Intro_Sends[IntroIndex]));
73 try {
74 dev.BulkRead(ep.read, response, 500);
76 catch( Usb::Timeout &to ) {
77 ddout("BulkRead: " << to.what());
78 return false;
80 ddout("BulkRead (" << (unsigned int)ep.read << "):\n" << response);
81 return true;
84 } // anonymous namespace
87 bool Probe::CheckSize(const Data &data, unsigned int required)
89 const unsigned char *pd = data.GetData();
91 if( GetSize(pd) != (unsigned int) data.GetSize() ||
92 data.GetSize() < required ||
93 pd[4] != SB_COMMAND_FETCHED_ATTRIBUTE )
95 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd)
96 << ", data.GetSize(): " << data.GetSize()
97 << ", pd[4]: " << (unsigned int) pd[4]);
98 return false;
101 return true;
104 bool Probe::ParsePIN(const Data &data, uint32_t &pin)
106 // validate response data
107 const unsigned char *pd = data.GetData();
109 if( !CheckSize(data, 0x14) )
110 return false;
112 // capture the PIN
113 memcpy(&pin, &pd[16], sizeof(pin));
114 pin = btohl(pin);
116 return true;
119 bool Probe::ParseDesc(const Data &data, std::string &desc)
121 if( !CheckSize(data, 29) )
122 return false;
124 // capture the description
125 const char *d = (const char*) &data.GetData()[28];
126 int maxlen = data.GetSize() - 28;
127 desc.assign(d, strnlen(d, maxlen));
129 return true;
132 Probe::Probe(const char *busname, const char *devname,
133 const Usb::EndpointPair *epp,
134 unsigned int log_exceptions,
135 bool auto_dump_log)
136 : m_log_exceptions(log_exceptions)
137 , m_fail_count(0)
138 , m_epp_override(epp)
140 if( m_epp_override ) {
141 m_epp = *epp;
144 // let the programmer pass in "" as well as 0
145 if( busname && !strlen(busname) )
146 busname = 0;
147 if( devname && !strlen(devname) )
148 devname = 0;
150 // Search for standard product ID first
151 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_BLACKBERRY, busname, devname);
153 // Search for Pearl devices second
155 // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
156 // the USB class 255 interface we need, but only the
157 // Mass Storage one. Here we search for PRODUCT_RIM_PEARL_DUAL,
158 // (ID 4) which has both enabled.
159 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_DUAL, busname, devname);
160 // And a special case, which behaves similar to the PEARL_DUAL,
161 // but with a unique Product ID.
162 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_8120, busname, devname);
163 // And one more! The Pearl Flip
164 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_FLIP, busname, devname);
166 // And one more time, for the Blackberry Storm
167 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_STORM, busname, devname);
169 // now dump all logged error messages
170 if( auto_dump_log && m_fail_msgs.size() ) {
171 eout("Probe logged " << m_fail_msgs.size() << " exception messages:");
172 for( std::vector<std::string>::const_iterator b = m_fail_msgs.begin();
173 b != m_fail_msgs.end();
174 ++b )
176 eout(*b);
181 void Probe::ProbeMatching(int vendor, int product,
182 const char *busname, const char *devname)
184 Usb::DeviceID devid;
186 Match match(m_devices, vendor, product, busname, devname);
188 while( match.next_device(devid) ) try {
189 ProbeDevice(devid);
191 catch( Usb::Error &e ) {
192 using namespace std;
194 dout("Usb::Error exception caught: " << e.what());
195 if( ((m_log_exceptions & LOG_BUSY) && e.system_errcode() == -EBUSY) ||
196 ((m_log_exceptions & LOG_ACCESS) && e.system_errcode() == -EACCES) ||
197 ((m_log_exceptions & LOG_PERM) && e.system_errcode() == -EPERM) )
199 m_fail_count++;
200 string msg = "Device " + devid.GetUsbName() + ": ";
201 if( e.system_errcode() == -EBUSY )
202 msg += "(EBUSY) ";
203 else if( e.system_errcode() == -EACCES )
204 msg += "(EACCES) ";
205 else if( e.system_errcode() == -EPERM )
206 msg += "(EPERM) ";
207 msg += e.what();
209 m_fail_msgs.push_back(msg);
211 else {
212 throw;
217 void Probe::ProbeDevice(Usb::DeviceID& devid)
219 // skip if we can't properly discover device config
220 DeviceDescriptor desc(devid);
221 ConfigDescriptor* config = desc[BLACKBERRY_CONFIGURATION];
222 if( !config ) {
223 dout("Probe: No device descriptor for BlackBerry config (config id: "
224 << BLACKBERRY_CONFIGURATION << ")");
225 return; // not found
228 // search for interface class
229 ConfigDescriptor::base_type::iterator idi = config->begin();
230 for( ; idi != config->end(); idi++ ) {
231 if( idi->second->GetClass() == BLACKBERRY_DB_CLASS )
232 break;
234 if( idi == config->end() ) {
235 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
236 << BLACKBERRY_DB_CLASS << ") not found.");
237 return; // not found
240 unsigned char InterfaceNumber = idi->second->GetNumber();
241 unsigned char InterfaceAltSetting = idi->second->GetAltSetting();
242 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber <<
243 " AltSetting: " << (unsigned int) InterfaceAltSetting);
245 // check endpoint validity
246 EndpointPairings ep(*(*config)[InterfaceNumber]);
247 if( !ep.IsValid() || ep.size() == 0 ) {
248 dout("Probe: endpoint invalid. ep.IsValid() == "
249 << (ep.IsValid() ? "true" : "false")
250 << ", ep.size() == "
251 << ep.size());
252 return;
255 ProbeResult result;
256 result.m_dev = devid;
257 result.m_class = BLACKBERRY_DB_CLASS;
258 result.m_interface = InterfaceNumber;
259 result.m_altsetting = InterfaceAltSetting;
260 result.m_zeroSocketSequence = 0;
262 // open device
263 Device dev(devid);
264 // dev.Reset();
265 // sleep(5);
267 // make sure we're talking to the right config
268 unsigned char cfg;
269 if( !dev.GetConfiguration(cfg) )
270 throw Usb::Error(dev.GetLastError(),
271 "Probe: GetConfiguration failed");
272 if( cfg != BLACKBERRY_CONFIGURATION || MUST_SET_CONFIGURATION ) {
273 if( !dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
274 throw Usb::Error(dev.GetLastError(),
275 "Probe: SetConfiguration failed");
278 // open interface
279 Interface iface(dev, InterfaceNumber);
281 // Try the initial probing of endpoints
282 ProbeDeviceEndpoints(dev, ep, result);
284 if( !result.m_ep.IsComplete() ) {
285 // Probing of end-points failed, so try reprobing
286 // after calling usb_set_altinterface().
288 // Calling usb_set_altinterface() should be harmless
289 // and can help the host and device to synchronize the
290 // USB state, especially on FreeBSD and Mac OS X.
291 // However it can cause usb-storage URBs to be lost
292 // on some devices, so is only used if necessary.
293 dout("Probe: probing endpoints failed, retrying after setting alternate interface");
295 iface.SetAltInterface(InterfaceAltSetting);
296 result.m_needSetAltInterface = true;
297 ProbeDeviceEndpoints(dev, ep, result);
300 // add to list
301 if( result.m_ep.IsComplete() ) {
302 // before adding to list, try to load the device's
303 // friendly name from the configfile... but don't
304 // fail if we can't do it
305 try {
306 ConfigFile cfg(result.m_pin);
307 result.m_cfgDeviceName = cfg.GetDeviceName();
309 catch( Barry::ConfigFileError & ) {
310 // ignore...
313 m_results.push_back(result);
314 ddout("Using ReadEndpoint: " << (unsigned int)result.m_ep.read);
315 ddout(" WriteEndpoint: " << (unsigned int)result.m_ep.write);
317 else {
318 ddout("Unable to discover endpoint pair for one device.");
322 void Probe::ProbeDeviceEndpoints(Device &dev, EndpointPairings &ed, ProbeResult &result)
324 if( m_epp_override ) {
325 // user has given us endpoints to try... so try them
326 uint32_t pin;
327 uint8_t zeroSocketSequence;
328 std::string desc;
329 bool needClearHalt;
330 if( ProbePair(dev, m_epp, pin, desc, zeroSocketSequence, needClearHalt) ) {
331 // looks good, finish filling out the result
332 result.m_ep = m_epp;
333 result.m_pin = pin;
334 result.m_description = desc;
335 result.m_zeroSocketSequence = zeroSocketSequence;
336 result.m_needClearHalt = needClearHalt;
339 else {
340 // find the first bulk read/write endpoint pair that answers
341 // to our probe commands
342 // Start with second pair, since evidence indicates the later pairs
343 // are the ones we need.
344 size_t i;
345 for(i = ed.size() > 1 ? 1 : 0;
346 i < ed.size();
347 i++ )
349 const EndpointPair &ep = ed[i];
350 if( ep.type == Usb::EndpointDescriptor::BulkType ) {
352 uint32_t pin;
353 uint8_t zeroSocketSequence;
354 std::string desc;
355 bool needClearHalt;
356 if( ProbePair(dev, ep, pin, desc, zeroSocketSequence, needClearHalt) ) {
357 result.m_ep = ep;
358 result.m_pin = pin;
359 result.m_description = desc;
360 result.m_zeroSocketSequence = zeroSocketSequence;
361 result.m_needClearHalt = needClearHalt;
362 break;
365 else {
366 dout("Probe: Skipping non-bulk endpoint pair (offset: "
367 << i-1 << ") ");
371 // check for ip modem endpoints
372 i++;
373 if( i < ed.size() ) {
374 const EndpointPair &ep = ed[i];
375 if( ProbeModem(dev, ep) ) {
376 result.m_epModem = ep;
382 bool Probe::ProbePair(Usb::Device &dev,
383 const Usb::EndpointPair &ep,
384 uint32_t &pin,
385 std::string &desc,
386 uint8_t &zeroSocketSequence,
387 bool &needClearHalt)
389 // Initially assume that clear halt isn't needed as it causes some
390 // devices to drop packets. The suspicion is that the toggle bits
391 // get out of sync, but this hasn't been confirmed with hardware
392 // tracing.
394 // It is possible to always use clear halt, as long as SET
395 // INTERFACE has been sent before, via usb_set_altinterface().
396 // However this has the side affect that any outstanding URBs
397 // on other interfaces (i.e. usb-storage) timeout and lose
398 // their data. This is not a good thing as it can corrupt the
399 // file system exposed over usb-storage. This also has the
400 // side-affect that usb-storage issues a port reset after the
401 // 30 second timeout, which kills any current Barry
402 // connection.
404 // To further complicate matters some devices, such as the
405 // 8830, always need clear halt before they will respond to
406 // probes.
408 // So to work with all these device quirks the probe is first
409 // attempted without calling clear halt. If that probe fails
410 // then a clear halt is issued followed by a retry on the
411 // probing.
412 needClearHalt = false;
414 Data data;
415 dev.BulkDrain(ep.read);
416 if( !Intro(0, ep, dev, data) ) {
417 // Try clearing halt and then reprobing
418 dout("Probe: Intro(0) failed, retrying after clearing halt");
419 dev.ClearHalt(ep.read);
420 dev.ClearHalt(ep.write);
421 needClearHalt = true;
422 // Retry
423 dev.BulkDrain(ep.read);
424 if( !Intro(0, ep, dev, data) ) {
425 // Still no response so fail the probe
426 dout("Probe: Intro(0) still failed after clearing halt");
427 return false;
431 SocketZero socket(dev, ep.write, ep.read);
433 Data send, receive;
434 ZeroPacket packet(send, receive);
436 // unknown attribute: 0x14 / 0x01
437 packet.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN,
438 SB_ATTR_INITIAL_UNKNOWN);
439 socket.Send(packet);
441 // fetch PIN
442 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_PIN);
443 socket.Send(packet);
444 if( packet.ObjectID() != SB_OBJECT_PROFILE ||
445 packet.AttributeID() != SB_ATTR_PROFILE_PIN ||
446 !ParsePIN(receive, pin) )
448 dout("Probe: unable to fetch PIN");
449 return false;
452 // fetch Description
453 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_DESC);
454 socket.Send(packet);
455 // response ObjectID does not match request... :-/
456 if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
457 packet.AttributeID() != SB_ATTR_PROFILE_DESC ||
458 !ParseDesc(receive, desc) )
460 dout("Probe: unable to fetch description");
463 // more unknowns:
464 for( uint16_t attr = 5; attr < 9; attr++ ) {
465 packet.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN, attr);
466 socket.Send(packet);
467 // FIXME parse these responses, if they turn
468 // out to be important
471 // all info obtained!
472 zeroSocketSequence = socket.GetZeroSocketSequence();
473 return true;
476 bool Probe::ProbeModem(Usb::Device &dev, const Usb::EndpointPair &ep)
479 // This check is not needed for all devices. Some devices,
480 // like the 8700 have both the RIM_UsbSerData mode and IpModem mode.
482 // If this function is called, then we have extra endpoints,
483 // so might as well try them.
485 // FIXME - someday, we might wish to confirm that the endpoints
486 // work as a modem, and return true/false based on that test.
488 return true;
491 // Thanks to Rick Scott (XmBlackBerry:bb_usb.c) for reverse engineering this
492 // int num_read;
493 // char data[255];
494 // int local_errno;
496 // num_read = usb_control_msg(dev.GetHandle(),
497 // /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
498 // /* bRequest */ 0xa5,
499 // /* wValue */ 0,
500 // /* wIndex */ 1,
501 // /* data */ data,
502 // /* wLength */ sizeof(data),
503 // /* timeout */ 2000);
504 // local_errno = errno;
505 // if( num_read > 1 ) {
506 // if( data[0] == 0x02 ) {
507 // return true;
508 // }
509 // }
510 // return false;
513 int Probe::FindActive(Barry::Pin pin) const
515 return FindActive(m_results, pin);
518 int Probe::FindActive(const Barry::Probe::Results &results, Barry::Pin pin)
520 int i = Find(results, pin);
522 if( i == -1 && pin == 0 ) {
523 // can we default to a single device?
524 if( results.size() == 1 )
525 return 0; // yes!
528 return i;
531 int Probe::Find(const Results &results, Barry::Pin pin)
533 Barry::Probe::Results::const_iterator ci = results.begin();
534 for( int i = 0; ci != results.end(); i++, ++ci ) {
535 if( ci->m_pin == pin )
536 return i;
538 // PIN not found
539 return -1;
542 ProbePlayBook::ProbePlayBook(const char *busname, const char *devname,
543 const Usb::EndpointPair *epp)
544 : m_fail_count(0)
545 , m_epp_override(epp)
547 if( m_epp_override ) {
548 m_epp = *epp;
551 // let the programmer pass in "" as well as 0
552 if( busname && !strlen(busname) )
553 busname = 0;
554 if( devname && !strlen(devname) )
555 devname = 0;
557 // Search for standard product ID first
558 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PLAYBOOK_STORAGE, busname, devname);
561 void ProbePlayBook::ProbeDevice(Usb::DeviceID& devid)
563 // skip if we can't properly discover device config
564 DeviceDescriptor desc(devid);
565 ConfigDescriptor* config = desc[BLACKBERRY_CONFIGURATION];
567 // search for interface class
568 ConfigDescriptor::base_type::iterator idi = config->begin();
569 for( ; idi != config->end(); idi++ ) {
570 if( idi->second->GetClass() == BLACKBERRY_MASS_STORAGE_CLASS )
571 break;
573 if( idi == config->end() ) {
574 dout("Probe: Interface with BLACKBERRY_MASS_STORAGE_CLASS ("
575 << BLACKBERRY_MASS_STORAGE_CLASS << ") not found.");
576 return; // not found
579 unsigned char InterfaceNumber = idi->second->GetNumber();
580 unsigned char InterfaceAltSetting = idi->second->GetAltSetting();
581 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber <<
582 " AltSetting: " << (unsigned int) InterfaceAltSetting);
584 // check endpoint validity
585 EndpointPairings ep(*(*config)[InterfaceNumber]);
586 if( !ep.IsValid() || ep.size() == 0 ) {
587 dout("Probe: endpoint invalid. ep.IsValid() == "
588 << (ep.IsValid() ? "true" : "false")
589 << ", ep.size() == "
590 << ep.size());
591 return;
594 ProbeResult result;
595 result.m_dev = devid;
596 result.m_class = BLACKBERRY_MASS_STORAGE_CLASS;
597 result.m_interface = InterfaceNumber;
598 result.m_altsetting = InterfaceAltSetting;
599 result.m_zeroSocketSequence = 0;
600 // result.m_ep = ep;
601 // result.m_pin = pin;
602 // result.m_description = desc;
603 // result.m_zeroSocketSequence = zeroSocketSequence;
604 result.m_needClearHalt = false,
605 result.m_needSetAltInterface = true;
607 // open device
608 Device dev(devid);
609 // dev.Reset();
610 // sleep(5);
612 // make sure device is available
613 std::string DriverName;
614 if (dev.IsAttachKernelDriver(InterfaceNumber))
615 dev.DetachKernelDriver(InterfaceNumber);
617 // make sure we're talking to the right config
618 unsigned char cfg;
619 if( !dev.GetConfiguration(cfg) )
620 throw Usb::Error(dev.GetLastError(),
621 "Probe: GetConfiguration failed");
622 if( cfg != BLACKBERRY_CONFIGURATION || MUST_SET_CONFIGURATION ) {
623 if( !dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
624 throw Usb::Error(dev.GetLastError(),
625 "Probe: SetConfiguration failed");
628 // SCSI Mass Storage isn't interessant for a Linux users
629 // switch the device to network interface to be useable
630 char buf[2];
631 dev.ControlMsg(USB_TYPE_VENDOR + USB_RECIP_DEVICE + USB_ENDPOINT_IN,
632 0x00000a9, 0x000000c, 0x0000000, buf, sizeof(buf), 1000);
633 dev.ControlMsg(USB_TYPE_VENDOR + USB_RECIP_DEVICE + USB_ENDPOINT_IN,
634 0x00000a5, 0x0000000, 0x0000001, buf, sizeof(buf), 1000);
636 // open interface
637 Interface iface(dev, InterfaceNumber);
639 iface.SetAltInterface(InterfaceAltSetting);
641 ddout("Using ReadEndpoint: " << (unsigned int)result.m_ep.read);
642 ddout(" WriteEndpoint: " << (unsigned int)result.m_ep.write);
644 // We don't add the device, since the device doesn't exist now !
645 // The storage device becomes a network device :)
649 void ProbeResult::DumpAll(std::ostream &os) const
651 ios_format_state state(os);
653 os << *this
654 << ", Interface: 0x" << std::hex << (unsigned int) m_interface
655 << ", Endpoints: (read: 0x" << std::hex << (unsigned int) m_ep.read
656 << ", write: 0x" << std::hex << (unsigned int) m_ep.write
657 << ", type: 0x" << std::hex << (unsigned int) m_ep.type
658 << ", ZeroSocketSequence: 0x" << std::hex << (unsigned int) m_zeroSocketSequence;
661 std::string ProbeResult::GetDisplayName() const
663 std::ostringstream oss;
664 oss << m_pin.Str();
665 if( m_cfgDeviceName.size() )
666 oss << " (" << m_cfgDeviceName << ")";
667 return oss.str();
670 std::ostream& operator<< (std::ostream &os, const ProbeResult &pr)
672 ios_format_state state(os);
674 os << "Device ID: " << pr.m_dev.m_impl.get()
675 << ". PIN: " << pr.m_pin.Str()
676 << ", Description: " << pr.m_description;
677 if( pr.m_cfgDeviceName.size() )
678 os << ", Name: " << pr.m_cfgDeviceName;
679 return os;
682 } // namespace Barry