3 /// USB Blackberry detection routines
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.
33 #include "protostructs.h"
34 #include "record-internal.h"
36 #include "configfile.h"
42 #include "ios_state.h"
48 unsigned char Intro_Sends
[][32] = {
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 }
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
]));
74 dev
.BulkRead(ep
.read
, response
, 500);
76 catch( Usb::Timeout
&to
) {
77 ddout("BulkRead: " << to
.what());
80 ddout("BulkRead (" << (unsigned int)ep
.read
<< "):\n" << response
);
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]);
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) )
113 memcpy(&pin
, &pd
[16], sizeof(pin
));
119 bool Probe::ParseDesc(const Data
&data
, std::string
&desc
)
121 if( !CheckSize(data
, 29) )
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
));
132 Probe::Probe(const char *busname
, const char *devname
,
133 const Usb::EndpointPair
*epp
,
134 unsigned int log_exceptions
,
136 : m_log_exceptions(log_exceptions
)
138 , m_epp_override(epp
)
140 if( m_epp_override
) {
144 // let the programmer pass in "" as well as 0
145 if( busname
&& !strlen(busname
) )
147 if( devname
&& !strlen(devname
) )
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();
181 void Probe::ProbeMatching(int vendor
, int product
,
182 const char *busname
, const char *devname
)
186 Match
match(m_devices
, vendor
, product
, busname
, devname
);
188 while( match
.next_device(devid
) ) try {
191 catch( Usb::Error
&e
) {
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
) )
200 string msg
= "Device " + devid
.GetUsbName() + ": ";
201 if( e
.system_errcode() == -EBUSY
)
203 else if( e
.system_errcode() == -EACCES
)
205 else if( e
.system_errcode() == -EPERM
)
209 m_fail_msgs
.push_back(msg
);
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
];
223 dout("Probe: No device descriptor for BlackBerry config (config id: "
224 << BLACKBERRY_CONFIGURATION
<< ")");
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
)
234 if( idi
== config
->end() ) {
235 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
236 << BLACKBERRY_DB_CLASS
<< ") 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")
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;
267 // make sure we're talking to the right config
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");
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
);
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
306 ConfigFile
cfg(result
.m_pin
);
307 result
.m_cfgDeviceName
= cfg
.GetDeviceName();
309 catch( Barry::ConfigFileError
& ) {
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
);
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
327 uint8_t zeroSocketSequence
;
330 if( ProbePair(dev
, m_epp
, pin
, desc
, zeroSocketSequence
, needClearHalt
) ) {
331 // looks good, finish filling out the result
334 result
.m_description
= desc
;
335 result
.m_zeroSocketSequence
= zeroSocketSequence
;
336 result
.m_needClearHalt
= needClearHalt
;
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.
345 for(i
= ed
.size() > 1 ? 1 : 0;
349 const EndpointPair
&ep
= ed
[i
];
350 if( ep
.type
== Usb::EndpointDescriptor::BulkType
) {
353 uint8_t zeroSocketSequence
;
356 if( ProbePair(dev
, ep
, pin
, desc
, zeroSocketSequence
, needClearHalt
) ) {
359 result
.m_description
= desc
;
360 result
.m_zeroSocketSequence
= zeroSocketSequence
;
361 result
.m_needClearHalt
= needClearHalt
;
366 dout("Probe: Skipping non-bulk endpoint pair (offset: "
371 // check for ip modem endpoints
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
,
386 uint8_t &zeroSocketSequence
,
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
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
404 // To further complicate matters some devices, such as the
405 // 8830, always need clear halt before they will respond to
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
412 needClearHalt
= false;
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;
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");
431 SocketZero
socket(dev
, ep
.write
, ep
.read
);
434 ZeroPacket
packet(send
, receive
);
436 // unknown attribute: 0x14 / 0x01
437 packet
.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN
,
438 SB_ATTR_INITIAL_UNKNOWN
);
442 packet
.GetAttribute(SB_OBJECT_PROFILE
, SB_ATTR_PROFILE_PIN
);
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");
453 packet
.GetAttribute(SB_OBJECT_PROFILE
, SB_ATTR_PROFILE_DESC
);
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");
464 for( uint16_t attr
= 5; attr
< 9; attr
++ ) {
465 packet
.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN
, attr
);
467 // FIXME parse these responses, if they turn
468 // out to be important
471 // all info obtained!
472 zeroSocketSequence
= socket
.GetZeroSocketSequence();
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.
491 // Thanks to Rick Scott (XmBlackBerry:bb_usb.c) for reverse engineering this
496 // num_read = usb_control_msg(dev.GetHandle(),
497 // /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
498 // /* bRequest */ 0xa5,
502 // /* wLength */ sizeof(data),
503 // /* timeout */ 2000);
504 // local_errno = errno;
505 // if( num_read > 1 ) {
506 // if( data[0] == 0x02 ) {
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 )
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
)
542 ProbePlayBook::ProbePlayBook(const char *busname
, const char *devname
,
543 const Usb::EndpointPair
*epp
)
545 , m_epp_override(epp
)
547 if( m_epp_override
) {
551 // let the programmer pass in "" as well as 0
552 if( busname
&& !strlen(busname
) )
554 if( devname
&& !strlen(devname
) )
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
)
573 if( idi
== config
->end() ) {
574 dout("Probe: Interface with BLACKBERRY_MASS_STORAGE_CLASS ("
575 << BLACKBERRY_MASS_STORAGE_CLASS
<< ") 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")
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;
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;
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
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
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);
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
);
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
;
665 if( m_cfgDeviceName
.size() )
666 oss
<< " (" << m_cfgDeviceName
<< ")";
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
;