3 /// USB Blackberry detection routines
7 Copyright (C) 2005-2010, 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.
32 #include "record-internal.h"
34 #include "configfile.h"
44 unsigned char Intro_Sends
[][32] = {
46 { 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0x00, 0x00,
47 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
51 unsigned char Intro_Receives
[][32] = {
52 // response to packet #1
53 { 0x00, 0x00, 0x10, 0x00, 0x02, 0xff, 0x00, 0x00,
54 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
59 unsigned int GetSize(const unsigned char *packet
)
61 uint16_t size
= *((uint16_t *)&packet
[2]);
65 bool Intro(int IntroIndex
, const EndpointPair
&ep
, Device
&dev
, Data
&response
)
67 dev
.BulkWrite(ep
.write
, Intro_Sends
[IntroIndex
],
68 GetSize(Intro_Sends
[IntroIndex
]));
70 dev
.BulkRead(ep
.read
, response
, 500);
72 catch( Usb::Timeout
&to
) {
73 ddout("BulkRead: " << to
.what());
76 ddout("BulkRead (" << (unsigned int)ep
.read
<< "):\n" << response
);
80 } // anonymous namespace
83 bool Probe::CheckSize(const Data
&data
, unsigned int required
)
85 const unsigned char *pd
= data
.GetData();
87 if( GetSize(pd
) != (unsigned int) data
.GetSize() ||
88 data
.GetSize() < required
||
89 pd
[4] != SB_COMMAND_FETCHED_ATTRIBUTE
)
91 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd
)
92 << ", data.GetSize(): " << data
.GetSize()
93 << ", pd[4]: " << (unsigned int) pd
[4]);
100 bool Probe::ParsePIN(const Data
&data
, uint32_t &pin
)
102 // validate response data
103 const unsigned char *pd
= data
.GetData();
105 if( !CheckSize(data
, 0x14) )
109 pin
= btohl(*((uint32_t *) &pd
[16]));
114 bool Probe::ParseDesc(const Data
&data
, std::string
&desc
)
116 if( !CheckSize(data
, 29) )
119 // capture the description
120 const char *d
= (const char*) &data
.GetData()[28];
121 int maxlen
= data
.GetSize() - 28;
122 desc
.assign(d
, strnlen(d
, maxlen
));
127 Probe::Probe(const char *busname
, const char *devname
,
128 const Usb::EndpointPair
*epp
)
130 , m_epp_override(epp
)
132 if( m_epp_override
) {
136 // let the programmer pass in "" as well as 0
137 if( busname
&& !strlen(busname
) )
139 if( devname
&& !strlen(devname
) )
142 // Search for standard product ID first
143 ProbeMatching(VENDOR_RIM
, PRODUCT_RIM_BLACKBERRY
, busname
, devname
);
145 // Search for Pearl devices second
147 // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
148 // the USB class 255 interface we need, but only the
149 // Mass Storage one. Here we search for PRODUCT_RIM_PEARL_DUAL,
150 // (ID 4) which has both enabled.
151 ProbeMatching(VENDOR_RIM
, PRODUCT_RIM_PEARL_DUAL
, busname
, devname
);
152 // And a special case, which behaves similar to the PEARL_DUAL,
153 // but with a unique Product ID.
154 ProbeMatching(VENDOR_RIM
, PRODUCT_RIM_PEARL_8120
, busname
, devname
);
155 // And one more! The Pearl Flip
156 ProbeMatching(VENDOR_RIM
, PRODUCT_RIM_PEARL_FLIP
, busname
, devname
);
158 // And one more time, for the Blackberry Storm
159 ProbeMatching(VENDOR_RIM
, PRODUCT_RIM_STORM
, busname
, devname
);
162 void Probe::ProbeMatching(int vendor
, int product
,
163 const char *busname
, const char *devname
)
165 Usb::DeviceIDType devid
;
167 Match
match(vendor
, product
, busname
, devname
);
168 while( match
.next_device(&devid
) ) try {
171 catch( Usb::Error
&e
) {
172 dout("Usb::Error exception caught: " << e
.what());
173 if( e
.libusb_errcode() == -EBUSY
) {
175 m_fail_msgs
.push_back(e
.what());
183 void Probe::ProbeDevice(Usb::DeviceIDType devid
)
185 // skip if we can't properly discover device config
186 DeviceDiscovery
discover(devid
);
187 ConfigDesc
&config
= discover
.configs
[BLACKBERRY_CONFIGURATION
];
189 // search for interface class
190 InterfaceDiscovery::base_type::iterator idi
= config
.interfaces
.begin();
191 for( ; idi
!= config
.interfaces
.end(); idi
++ ) {
192 if( idi
->second
.desc
.bInterfaceClass
== BLACKBERRY_DB_CLASS
)
195 if( idi
== config
.interfaces
.end() ) {
196 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
197 << BLACKBERRY_DB_CLASS
<< ") not found.");
201 unsigned char InterfaceNumber
= idi
->second
.desc
.bInterfaceNumber
;
202 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber
);
204 // check endpoint validity
205 EndpointDiscovery
&ed
= config
.interfaces
[InterfaceNumber
].endpoints
;
206 if( !ed
.IsValid() || ed
.GetEndpointPairs().size() == 0 ) {
207 dout("Probe: endpoint invalid. ed.IsValud() == "
208 << (ed
.IsValid() ? "true" : "false")
209 << ", ed.GetEndpointPairs().size() == "
210 << ed
.GetEndpointPairs().size());
215 result
.m_dev
= devid
;
216 result
.m_interface
= InterfaceNumber
;
217 result
.m_zeroSocketSequence
= 0;
224 // make sure we're talking to the right config
226 if( !dev
.GetConfiguration(cfg
) )
227 throw Usb::Error(dev
.GetLastError(),
228 "Probe: GetConfiguration failed");
229 if( cfg
!= BLACKBERRY_CONFIGURATION
|| MUST_SET_CONFIGURATION
) {
230 if( !dev
.SetConfiguration(BLACKBERRY_CONFIGURATION
) )
231 throw Usb::Error(dev
.GetLastError(),
232 "Probe: SetConfiguration failed");
236 Interface
iface(dev
, InterfaceNumber
);
238 // Try the initial probing of endpoints
239 ProbeDeviceEndpoints(dev
, ed
, result
);
241 if( !result
.m_ep
.IsComplete() ) {
242 // Probing of end-points failed, so try reprobing
243 // after calling usb_set_altinterface().
245 // Calling usb_set_altinterface() should be harmless
246 // and can help the host and device to synchronize the
247 // USB state, especially on FreeBSD and Mac OS X.
248 // However it can cause usb-storage URBs to be lost
249 // on some devices, so is only used if necessary.
250 dout("Probe: probing endpoints failed, retrying after setting alternate interface");
251 dev
.SetAltInterface(InterfaceNumber
);
252 result
.m_needSetAltInterface
= true;
253 ProbeDeviceEndpoints(dev
, ed
, result
);
257 if( result
.m_ep
.IsComplete() ) {
258 // before adding to list, try to load the device's
259 // friendly name from the configfile... but don't
260 // fail if we can't do it
262 ConfigFile
cfg(result
.m_pin
);
263 result
.m_cfgDeviceName
= cfg
.GetDeviceName();
265 catch( Barry::ConfigFileError
& ) {
269 m_results
.push_back(result
);
270 ddout("Using ReadEndpoint: " << (unsigned int)result
.m_ep
.read
);
271 ddout(" WriteEndpoint: " << (unsigned int)result
.m_ep
.write
);
274 ddout("Unable to discover endpoint pair for one device.");
278 void Probe::ProbeDeviceEndpoints(Device
&dev
, EndpointDiscovery
&ed
, ProbeResult
&result
)
280 if( m_epp_override
) {
281 // user has given us endpoints to try... so try them
283 uint8_t zeroSocketSequence
;
286 if( ProbePair(dev
, m_epp
, pin
, desc
, zeroSocketSequence
, needClearHalt
) ) {
287 // looks good, finish filling out the result
290 result
.m_description
= desc
;
291 result
.m_zeroSocketSequence
= zeroSocketSequence
;
292 result
.m_needClearHalt
= needClearHalt
;
296 // find the first bulk read/write endpoint pair that answers
297 // to our probe commands
298 // Start with second pair, since evidence indicates the later pairs
299 // are the ones we need.
301 for(i
= ed
.GetEndpointPairs().size() > 1 ? 1 : 0;
302 i
< ed
.GetEndpointPairs().size();
305 const EndpointPair
&ep
= ed
.GetEndpointPairs()[i
];
306 if( ep
.type
== USB_ENDPOINT_TYPE_BULK
) {
309 uint8_t zeroSocketSequence
;
312 if( ProbePair(dev
, ep
, pin
, desc
, zeroSocketSequence
, needClearHalt
) ) {
315 result
.m_description
= desc
;
316 result
.m_zeroSocketSequence
= zeroSocketSequence
;
317 result
.m_needClearHalt
= needClearHalt
;
322 dout("Probe: Skipping non-bulk endpoint pair (offset: "
327 // check for ip modem endpoints
329 if( i
< ed
.GetEndpointPairs().size() ) {
330 const EndpointPair
&ep
= ed
.GetEndpointPairs()[i
];
331 if( ProbeModem(dev
, ep
) ) {
332 result
.m_epModem
= ep
;
338 bool Probe::ProbePair(Usb::Device
&dev
,
339 const Usb::EndpointPair
&ep
,
342 uint8_t &zeroSocketSequence
,
345 // Initially assume that clear halt isn't needed as it causes some
346 // devices to drop packets. The suspicion is that the toggle bits
347 // get out of sync, but this hasn't been confirmed with hardware
350 // It is possible to always use clear halt, as long as SET
351 // INTERFACE has been sent before, via usb_set_altinterface().
352 // However this has the side affect that any outstanding URBs
353 // on other interfaces (i.e. usb-storage) timeout and lose
354 // their data. This is not a good thing as it can corrupt the
355 // file system exposed over usb-storage. This also has the
356 // side-affect that usb-storage issues a port reset after the
357 // 30 second timeout, which kills any current Barry
360 // To further complicate matters some devices, such as the
361 // 8830, always need clear halt before they will respond to
364 // So to work with all these device quirks the probe is first
365 // attempted without calling clear halt. If that probe fails
366 // then a clear halt is issued followed by a retry on the
368 needClearHalt
= false;
371 dev
.BulkDrain(ep
.read
);
372 if( !Intro(0, ep
, dev
, data
) ) {
373 // Try clearing halt and then reprobing
374 dout("Probe: Intro(0) failed, retrying after clearing halt");
375 dev
.ClearHalt(ep
.read
);
376 dev
.ClearHalt(ep
.write
);
377 needClearHalt
= true;
379 dev
.BulkDrain(ep
.read
);
380 if( !Intro(0, ep
, dev
, data
) ) {
381 // Still no response so fail the probe
382 dout("Probe: Intro(0) still failed after clearing halt");
387 SocketZero
socket(dev
, ep
.write
, ep
.read
);
390 ZeroPacket
packet(send
, receive
);
392 // unknown attribute: 0x14 / 0x01
393 packet
.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN
,
394 SB_ATTR_INITIAL_UNKNOWN
);
398 packet
.GetAttribute(SB_OBJECT_PROFILE
, SB_ATTR_PROFILE_PIN
);
400 if( packet
.ObjectID() != SB_OBJECT_PROFILE
||
401 packet
.AttributeID() != SB_ATTR_PROFILE_PIN
||
402 !ParsePIN(receive
, pin
) )
404 dout("Probe: unable to fetch PIN");
409 packet
.GetAttribute(SB_OBJECT_PROFILE
, SB_ATTR_PROFILE_DESC
);
411 // response ObjectID does not match request... :-/
412 if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
413 packet
.AttributeID() != SB_ATTR_PROFILE_DESC
||
414 !ParseDesc(receive
, desc
) )
416 dout("Probe: unable to fetch description");
420 for( uint16_t attr
= 5; attr
< 9; attr
++ ) {
421 packet
.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN
, attr
);
423 // FIXME parse these responses, if they turn
424 // out to be important
427 // all info obtained!
428 zeroSocketSequence
= socket
.GetZeroSocketSequence();
432 bool Probe::ProbeModem(Usb::Device
&dev
, const Usb::EndpointPair
&ep
)
435 // This check is not needed for all devices. Some devices,
436 // like the 8700 have both the RIM_UsbSerData mode and IpModem mode.
438 // If this function is called, then we have extra endpoints,
439 // so might as well try them.
441 // FIXME - someday, we might wish to confirm that the endpoints
442 // work as a modem, and return true/false based on that test.
447 // Thanks to Rick Scott (XmBlackBerry:bb_usb.c) for reverse engineering this
452 // num_read = usb_control_msg(dev.GetHandle(),
453 // /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
454 // /* bRequest */ 0xa5,
458 // /* wLength */ sizeof(data),
459 // /* timeout */ 2000);
460 // local_errno = errno;
461 // if( num_read > 1 ) {
462 // if( data[0] == 0x02 ) {
469 int Probe::FindActive(Barry::Pin pin
) const
471 return FindActive(m_results
, pin
);
474 int Probe::FindActive(const Barry::Probe::Results
&results
, Barry::Pin pin
)
476 int i
= Find(results
, pin
);
478 if( i
== -1 && pin
== 0 ) {
479 // can we default to a single device?
480 if( results
.size() == 1 )
487 int Probe::Find(const Results
&results
, Barry::Pin pin
)
489 Barry::Probe::Results::const_iterator ci
= results
.begin();
490 for( int i
= 0; ci
!= results
.end(); i
++, ++ci
) {
491 if( ci
->m_pin
== pin
)
498 void ProbeResult::DumpAll(std::ostream
&os
) const
501 << ", Interface: 0x" << std::hex
<< (unsigned int) m_interface
502 << ", Endpoints: (read: 0x" << std::hex
<< (unsigned int) m_ep
.read
503 << ", write: 0x" << std::hex
<< (unsigned int) m_ep
.write
504 << ", type: 0x" << std::hex
<< (unsigned int) m_ep
.type
505 << ", ZeroSocketSequence: 0x" << std::hex
<< (unsigned int) m_zeroSocketSequence
;
508 std::ostream
& operator<< (std::ostream
&os
, const ProbeResult
&pr
)
510 os
<< "Device ID: " << pr
.m_dev
511 << ". PIN: " << pr
.m_pin
.Str()
512 << ", Description: " << pr
.m_description
;
513 if( pr
.m_cfgDeviceName
.size() )
514 os
<< ", Name: " << pr
.m_cfgDeviceName
;