3 /// USB Blackberry detection routines
7 Copyright (C) 2005-2008, 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"
42 unsigned char Intro_Sends
[][32] = {
44 { 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0x00, 0x00,
45 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
49 unsigned char Intro_Receives
[][32] = {
50 // response to packet #1
51 { 0x00, 0x00, 0x10, 0x00, 0x02, 0xff, 0x00, 0x00,
52 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
57 unsigned int GetSize(const unsigned char *packet
)
59 uint16_t size
= *((uint16_t *)&packet
[2]);
63 bool Intro(int IntroIndex
, const EndpointPair
&ep
, Device
&dev
, Data
&response
)
65 dev
.BulkWrite(ep
.write
, Intro_Sends
[IntroIndex
],
66 GetSize(Intro_Sends
[IntroIndex
]));
68 dev
.BulkRead(ep
.read
, response
, 500);
70 catch( Usb::Timeout
&to
) {
71 ddout("BulkRead: " << to
.what());
74 ddout("BulkRead (" << (unsigned int)ep
.read
<< "):\n" << response
);
78 } // anonymous namespace
81 bool Probe::CheckSize(const Data
&data
, unsigned int required
)
83 const unsigned char *pd
= data
.GetData();
85 if( GetSize(pd
) != (unsigned int) data
.GetSize() ||
86 data
.GetSize() < required
||
87 pd
[4] != SB_COMMAND_FETCHED_ATTRIBUTE
)
89 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd
)
90 << ", data.GetSize(): " << data
.GetSize()
91 << ", pd[4]: " << (unsigned int) pd
[4]);
98 bool Probe::ParsePIN(const Data
&data
, uint32_t &pin
)
100 // validate response data
101 const unsigned char *pd
= data
.GetData();
103 if( !CheckSize(data
, 0x14) )
107 pin
= btohl(*((uint32_t *) &pd
[16]));
112 bool Probe::ParseDesc(const Data
&data
, std::string
&desc
)
114 if( !CheckSize(data
, 29) )
117 // capture the description
118 const char *d
= (const char*) &data
.GetData()[28];
119 int maxlen
= data
.GetSize() - 28;
120 desc
.assign(d
, strnlen(d
, maxlen
));
125 Probe::Probe(const char *busname
, const char *devname
)
128 // let the programmer pass in "" as well as 0
129 if( busname
&& !strlen(busname
) )
131 if( devname
&& !strlen(devname
) )
134 // Search for standard product ID first
135 ProbeMatching(VENDOR_RIM
, PRODUCT_RIM_BLACKBERRY
, busname
, devname
);
137 // Search for Pearl devices second
139 // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
140 // the USB class 255 interface we need, but only the
141 // Mass Storage one. Here we search for PRODUCT_RIM_PEARL_DUAL,
142 // (ID 4) which has both enabled.
143 ProbeMatching(VENDOR_RIM
, PRODUCT_RIM_PEARL_DUAL
, busname
, devname
);
144 // And a special case, which behaves similar to the PEARL_DUAL,
145 // but with a unique Product ID.
146 ProbeMatching(VENDOR_RIM
, PRODUCT_RIM_PEARL_8120
, busname
, devname
);
149 void Probe::ProbeMatching(int vendor
, int product
,
150 const char *busname
, const char *devname
)
152 Usb::DeviceIDType devid
;
154 Match
match(vendor
, product
, busname
, devname
);
155 while( match
.next_device(&devid
) ) try {
158 catch( Usb::Error
&e
) {
159 dout("Usb::Error exception caught: " << e
.what());
160 if( e
.libusb_errcode() == -EBUSY
) {
162 m_fail_msgs
.push_back(e
.what());
170 void Probe::ProbeDevice(Usb::DeviceIDType devid
)
172 // skip if we can't properly discover device config
173 DeviceDiscovery
discover(devid
);
174 ConfigDesc
&config
= discover
.configs
[BLACKBERRY_CONFIGURATION
];
176 // search for interface class
177 InterfaceDiscovery::base_type::iterator idi
= config
.interfaces
.begin();
178 for( ; idi
!= config
.interfaces
.end(); idi
++ ) {
179 if( idi
->second
.desc
.bInterfaceClass
== BLACKBERRY_DB_CLASS
)
182 if( idi
== config
.interfaces
.end() ) {
183 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
184 << BLACKBERRY_DB_CLASS
<< ") not found.");
188 unsigned char InterfaceNumber
= idi
->second
.desc
.bInterfaceNumber
;
189 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber
);
191 // check endpoint validity
192 EndpointDiscovery
&ed
= config
.interfaces
[InterfaceNumber
].endpoints
;
193 if( !ed
.IsValid() || ed
.GetEndpointPairs().size() == 0 ) {
194 dout("Probe: endpoint invalid. ed.IsValud() == "
195 << (ed
.IsValid() ? "true" : "false")
196 << ", ed.GetEndpointPairs().size() == "
197 << ed
.GetEndpointPairs().size());
202 result
.m_dev
= devid
;
203 result
.m_interface
= InterfaceNumber
;
204 result
.m_zeroSocketSequence
= 0;
211 // make sure we're talking to the right config
213 if( !dev
.GetConfiguration(cfg
) )
214 throw Usb::Error(dev
.GetLastError(),
215 "Probe: GetConfiguration failed");
216 if( cfg
!= BLACKBERRY_CONFIGURATION
) {
217 if( !dev
.SetConfiguration(BLACKBERRY_CONFIGURATION
) )
218 throw Usb::Error(dev
.GetLastError(),
219 "Probe: SetConfiguration failed");
223 Interface
iface(dev
, InterfaceNumber
);
225 // find the first bulk read/write endpoint pair that answers
226 // to our probe commands
227 // Start with second pair, since evidence indicates the later pairs
228 // are the ones we need.
230 for(i
= ed
.GetEndpointPairs().size() > 1 ? 1 : 0;
231 i
< ed
.GetEndpointPairs().size();
234 const EndpointPair
&ep
= ed
.GetEndpointPairs()[i
];
235 if( ep
.type
== USB_ENDPOINT_TYPE_BULK
) {
238 uint8_t zeroSocketSequence
;
240 if( ProbePair(dev
, ep
, pin
, desc
, zeroSocketSequence
) ) {
243 result
.m_description
= desc
;
244 result
.m_zeroSocketSequence
= zeroSocketSequence
;
249 dout("Probe: Skipping non-bulk endpoint pair (offset: "
254 // check for ip modem endpoints
256 if( i
< ed
.GetEndpointPairs().size() ) {
257 const EndpointPair
&ep
= ed
.GetEndpointPairs()[i
];
258 if( ProbeModem(dev
, ep
) ) {
259 result
.m_epModem
= ep
;
264 if( result
.m_ep
.IsComplete() ) {
265 m_results
.push_back(result
);
266 ddout("Using ReadEndpoint: " << (unsigned int)result
.m_ep
.read
);
267 ddout(" WriteEndpoint: " << (unsigned int)result
.m_ep
.write
);
270 ddout("Unable to discover endpoint pair for one device.");
274 bool Probe::ProbePair(Usb::Device
&dev
,
275 const Usb::EndpointPair
&ep
,
278 uint8_t &zeroSocketSequence
)
280 dev
.ClearHalt(ep
.read
);
281 dev
.ClearHalt(ep
.write
);
284 dev
.BulkDrain(ep
.read
);
285 if( !Intro(0, ep
, dev
, data
) ) {
286 dout("Probe: Intro(0) failed");
290 SocketZero
socket(dev
, ep
.write
, ep
.read
);
293 ZeroPacket
packet(send
, receive
);
295 // unknown attribute: 0x14 / 0x01
296 packet
.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN
,
297 SB_ATTR_INITIAL_UNKNOWN
);
301 packet
.GetAttribute(SB_OBJECT_PROFILE
, SB_ATTR_PROFILE_PIN
);
303 if( packet
.ObjectID() != SB_OBJECT_PROFILE
||
304 packet
.AttributeID() != SB_ATTR_PROFILE_PIN
||
305 !ParsePIN(receive
, pin
) )
307 dout("Probe: unable to fetch PIN");
312 packet
.GetAttribute(SB_OBJECT_PROFILE
, SB_ATTR_PROFILE_DESC
);
314 // response ObjectID does not match request... :-/
315 if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
316 packet
.AttributeID() != SB_ATTR_PROFILE_DESC
||
317 !ParseDesc(receive
, desc
) )
319 dout("Probe: unable to fetch description");
323 for( uint16_t attr
= 5; attr
< 9; attr
++ ) {
324 packet
.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN
, attr
);
326 // FIXME parse these responses, if they turn
327 // out to be important
330 // all info obtained!
331 zeroSocketSequence
= socket
.GetZeroSocketSequence();
335 bool Probe::ProbeModem(Usb::Device
&dev
, const Usb::EndpointPair
&ep
)
338 // This check is not needed for all devices. Some devices,
339 // like the 8700 have both the RIM_UsbSerData mode and IpModem mode.
341 // If this function is called, then we have extra endpoints,
342 // so might as well try them.
344 // FIXME - someday, we might wish to confirm that the endpoints
345 // work as a modem, and return true/false based on that test.
350 // Thanks to Rick Scott (XmBlackBerry:bb_usb.c) for reverse engineering this
355 // num_read = usb_control_msg(dev.GetHandle(),
356 // /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
357 // /* bRequest */ 0xa5,
361 // /* wLength */ sizeof(data),
362 // /* timeout */ 2000);
363 // local_errno = errno;
364 // if( num_read > 1 ) {
365 // if( data[0] == 0x02 ) {
372 int Probe::FindActive(uint32_t pin
) const
374 for( int i
= 0; i
< GetCount(); i
++ ) {
375 if( Get(i
).m_pin
== pin
)
379 // can we default to a single device?
380 if( GetCount() == 1 )
388 void ProbeResult::DumpAll(std::ostream
&os
) const
391 << ", Interface: 0x" << std::hex
<< (unsigned int) m_interface
392 << ", Endpoints: (read: 0x" << std::hex
<< (unsigned int) m_ep
.read
393 << ", write: 0x" << std::hex
<< (unsigned int) m_ep
.write
394 << ", type: 0x" << std::hex
<< (unsigned int) m_ep
.type
395 << ", ZeroSocketSequence: 0x" << std::hex
<< (unsigned int) m_zeroSocketSequence
;
398 std::ostream
& operator<< (std::ostream
&os
, const ProbeResult
&pr
)
400 os
<< "Device ID: " << pr
.m_dev
401 << std::hex
<< ". PIN: " << pr
.m_pin
402 << ", Description: " << pr
.m_description
;