Release tarball for barry-0.9
[barry.git] / src / probe.cc
blob5f52176e8e2518ea2fa7f632f724ad81745d00fb
1 ///
2 /// \file probe.cc
3 /// USB Blackberry detection routines
4 ///
6 /*
7 Copyright (C) 2005-2007, 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 "record-internal.h"
33 #include "strnlen.h"
34 #include <iomanip>
36 using namespace Usb;
38 namespace Barry {
40 unsigned char Intro_Sends[][32] = {
41 // packet #1
42 { 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0x00, 0x00,
43 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
47 unsigned char Intro_Receives[][32] = {
48 // response to packet #1
49 { 0x00, 0x00, 0x10, 0x00, 0x02, 0xff, 0x00, 0x00,
50 0xa8, 0x18, 0xda, 0x8d, 0x6c, 0x02, 0x00, 0x00 }
53 namespace {
55 unsigned int GetSize(const unsigned char *packet)
57 uint16_t size = *((uint16_t *)&packet[2]);
58 return btohs(size);
61 bool Intro(int IntroIndex, const EndpointPair &ep, Device &dev, Data &response)
63 dev.BulkWrite(ep.write, Intro_Sends[IntroIndex],
64 GetSize(Intro_Sends[IntroIndex]));
65 dev.BulkRead(ep.read, response);
66 ddout("BulkRead (" << (unsigned int)ep.read << "):\n" << response);
67 return true;
70 } // anonymous namespace
73 bool Probe::CheckSize(const Data &data, unsigned int required)
75 const unsigned char *pd = data.GetData();
77 if( GetSize(pd) != (unsigned int) data.GetSize() ||
78 data.GetSize() < required ||
79 pd[4] != SB_COMMAND_FETCHED_ATTRIBUTE )
81 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd)
82 << ", data.GetSize(): " << data.GetSize()
83 << ", pd[4]: " << (unsigned int) pd[4]);
84 return false;
87 return true;
90 bool Probe::ParsePIN(const Data &data, ProbeResult &result)
92 // validate response data
93 const unsigned char *pd = data.GetData();
95 if( !CheckSize(data, 0x14) )
96 return false;
98 // capture the PIN
99 result.m_pin = btohl(*((uint32_t *) &pd[16]));
101 return true;
104 bool Probe::ParseDesc(const Data &data, ProbeResult &result)
106 if( !CheckSize(data, 29) )
107 return false;
109 // capture the description
110 const char *desc = (const char*) &data.GetData()[28];
111 int maxlen = data.GetSize() - 28;
112 result.m_description.assign(desc, strnlen(desc, maxlen));
114 return true;
117 Probe::Probe()
119 Usb::DeviceIDType devid;
121 // Search for standard product ID first
123 Match match(VENDOR_RIM, PRODUCT_RIM_BLACKBERRY);
124 while( match.next_device(&devid) )
125 ProbeDevice(devid);
128 // Search for Pearl devices second
130 // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
131 // the USB class 255 interface we need, but only the
132 // Mass Storage one. Here we search for PRODUCT_RIM_PEARL_DUAL,
133 // (ID 4) which has both enabled.
134 Match match(VENDOR_RIM, PRODUCT_RIM_PEARL_DUAL);
135 while( match.next_device(&devid) )
136 ProbeDevice(devid);
140 void Probe::ProbeDevice(Usb::DeviceIDType devid)
142 // skip if we can't properly discover device config
143 DeviceDiscovery discover(devid);
144 ConfigDesc &config = discover.configs[BLACKBERRY_CONFIGURATION];
146 // search for interface class
147 InterfaceDiscovery::base_type::iterator i = config.interfaces.begin();
148 for( ; i != config.interfaces.end(); i++ ) {
149 if( i->second.desc.bInterfaceClass == BLACKBERRY_DB_CLASS )
150 break;
152 if( i == config.interfaces.end() ) {
153 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
154 << BLACKBERRY_DB_CLASS << ") not found.");
155 return; // not found
158 unsigned char InterfaceNumber = i->second.desc.bInterfaceNumber;
159 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber);
161 // check endpoint validity
162 EndpointDiscovery &ed = config.interfaces[InterfaceNumber].endpoints;
163 if( !ed.IsValid() || ed.GetEndpointPairs().size() == 0 ) {
164 dout("Probe: endpoint invalid. ed.IsValud() == "
165 << (ed.IsValid() ? "true" : "false")
166 << ", ed.GetEndpointPairs().size() == "
167 << ed.GetEndpointPairs().size());
168 return;
171 ProbeResult result;
172 result.m_dev = devid;
173 result.m_interface = InterfaceNumber;
174 result.m_zeroSocketSequence = 0;
176 // find the first bulk read/write endpoint pair that answers
177 // to our probe commands
178 // Start with second pair, since evidence indicates the later pairs
179 // are the ones we need.
180 for(size_t i = ed.GetEndpointPairs().size() > 1 ? 1 : 0;
181 i < ed.GetEndpointPairs().size();
182 i++ )
184 const EndpointPair &ep = ed.GetEndpointPairs()[i];
185 if( ep.type == USB_ENDPOINT_TYPE_BULK ) {
186 result.m_ep = ep;
188 Device dev(devid);
189 // dev.Reset();
190 // sleep(5);
192 if( !dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
193 throw Usb::Error(dev.GetLastError(),
194 "Probe: SetConfiguration failed");
196 Interface iface(dev, InterfaceNumber);
198 Data data;
199 dev.BulkDrain(ep.read);
200 if( !Intro(0, ep, dev, data) ) {
201 dout("Probe: Intro(0) failed");
202 continue;
205 Socket socket(dev, ep.write, ep.read);
207 Data send, receive;
208 ZeroPacket packet(send, receive);
210 // unknown attribute: 0x14 / 0x01
211 packet.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN,
212 SB_ATTR_INITIAL_UNKNOWN);
213 socket.Send(packet);
215 // fetch PIN
216 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_PIN);
217 socket.Send(packet);
218 if( packet.ObjectID() != SB_OBJECT_PROFILE ||
219 packet.AttributeID() != SB_ATTR_PROFILE_PIN ||
220 !ParsePIN(receive, result) )
222 dout("Probe: unable to fetch PIN");
223 continue;
226 // fetch Description
227 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_DESC);
228 socket.Send(packet);
229 // response ObjectID does not match request... :-/
230 if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
231 packet.AttributeID() != SB_ATTR_PROFILE_DESC ||
232 !ParseDesc(receive, result) )
234 dout("Probe: unable to fetch description");
235 // this is a relatively new feature, so don't
236 // fail here... just blank the description
237 result.m_description.clear();
240 // more unknowns:
241 for( uint16_t attr = 5; attr < 9; attr++ ) {
242 packet.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN, attr);
243 socket.Send(packet);
244 // FIXME parse these responses, if they turn
245 // out to be important
249 // all info obtained, add to list
250 result.m_zeroSocketSequence = socket.GetZeroSocketSequence();
251 m_results.push_back(result);
252 ddout("Using ReadEndpoint: " << (unsigned int)result.m_ep.read);
253 ddout(" WriteEndpoint: " << (unsigned int)result.m_ep.write);
254 break;
256 else {
257 dout("Probe: Skipping non-bulk endpoint pair (offset: "
258 << i-1 << ") ");
262 if( !result.m_ep.IsComplete() )
263 ddout("Unable to discover endpoint pair for one device.");
266 int Probe::FindActive(uint32_t pin) const
268 for( int i = 0; i < GetCount(); i++ ) {
269 if( Get(i).m_pin == pin )
270 return i;
272 if( pin == 0 ) {
273 // can we default to a single device?
274 if( GetCount() == 1 )
275 return 0; // yes!
278 // PIN not found
279 return -1;
282 std::ostream& operator<< (std::ostream &os, const ProbeResult &pr)
284 os << "Device ID: " << pr.m_dev
285 << std::hex << ". PIN: " << pr.m_pin
286 << ", Description: " << pr.m_description;
287 return os;
290 } // namespace Barry