Added Debian source package support.
[barry/pauldeden.git] / src / probe.cc
blobec3ebdd14b7749010e96e8c770ba4545338d2c33
1 ///
2 /// \file probe.cc
3 /// USB Blackberry detection routines
4 ///
6 /*
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.
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>
35 #include <errno.h>
36 #include <string.h>
38 using namespace Usb;
40 namespace Barry {
42 unsigned char Intro_Sends[][32] = {
43 // packet #1
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 }
55 namespace {
57 unsigned int GetSize(const unsigned char *packet)
59 uint16_t size = *((uint16_t *)&packet[2]);
60 return btohs(size);
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]));
67 dev.BulkRead(ep.read, response);
68 ddout("BulkRead (" << (unsigned int)ep.read << "):\n" << response);
69 return true;
72 } // anonymous namespace
75 bool Probe::CheckSize(const Data &data, unsigned int required)
77 const unsigned char *pd = data.GetData();
79 if( GetSize(pd) != (unsigned int) data.GetSize() ||
80 data.GetSize() < required ||
81 pd[4] != SB_COMMAND_FETCHED_ATTRIBUTE )
83 dout("Probe: Parse data failure: GetSize(pd): " << GetSize(pd)
84 << ", data.GetSize(): " << data.GetSize()
85 << ", pd[4]: " << (unsigned int) pd[4]);
86 return false;
89 return true;
92 bool Probe::ParsePIN(const Data &data, ProbeResult &result)
94 // validate response data
95 const unsigned char *pd = data.GetData();
97 if( !CheckSize(data, 0x14) )
98 return false;
100 // capture the PIN
101 result.m_pin = btohl(*((uint32_t *) &pd[16]));
103 return true;
106 bool Probe::ParseDesc(const Data &data, ProbeResult &result)
108 if( !CheckSize(data, 29) )
109 return false;
111 // capture the description
112 const char *desc = (const char*) &data.GetData()[28];
113 int maxlen = data.GetSize() - 28;
114 result.m_description.assign(desc, strnlen(desc, maxlen));
116 return true;
119 Probe::Probe(const char *busname, const char *devname)
120 : m_fail_count(0)
122 // let the programmer pass in "" as well as 0
123 if( busname && !strlen(busname) )
124 busname = 0;
125 if( devname && !strlen(devname) )
126 devname = 0;
128 // Search for standard product ID first
129 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_BLACKBERRY, busname, devname);
131 // Search for Pearl devices second
133 // productID 6 devices (PRODUCT_RIM_PEARL) do not expose
134 // the USB class 255 interface we need, but only the
135 // Mass Storage one. Here we search for PRODUCT_RIM_PEARL_DUAL,
136 // (ID 4) which has both enabled.
137 ProbeMatching(VENDOR_RIM, PRODUCT_RIM_PEARL_DUAL, busname, devname);
140 void Probe::ProbeMatching(int vendor, int product,
141 const char *busname, const char *devname)
143 Usb::DeviceIDType devid;
145 Match match(vendor, product, busname, devname);
146 while( match.next_device(&devid) ) try {
147 ProbeDevice(devid);
149 catch( Usb::Error &e ) {
150 dout("Usb::Error exception caught: " << e.what());
151 if( e.libusb_errcode() == -EBUSY ) {
152 m_fail_count++;
153 m_fail_msgs.push_back(e.what());
155 else {
156 throw;
161 void Probe::ProbeDevice(Usb::DeviceIDType devid)
163 // skip if we can't properly discover device config
164 DeviceDiscovery discover(devid);
165 ConfigDesc &config = discover.configs[BLACKBERRY_CONFIGURATION];
167 // search for interface class
168 InterfaceDiscovery::base_type::iterator i = config.interfaces.begin();
169 for( ; i != config.interfaces.end(); i++ ) {
170 if( i->second.desc.bInterfaceClass == BLACKBERRY_DB_CLASS )
171 break;
173 if( i == config.interfaces.end() ) {
174 dout("Probe: Interface with BLACKBERRY_DB_CLASS ("
175 << BLACKBERRY_DB_CLASS << ") not found.");
176 return; // not found
179 unsigned char InterfaceNumber = i->second.desc.bInterfaceNumber;
180 dout("Probe: using InterfaceNumber: " << (unsigned int) InterfaceNumber);
182 // check endpoint validity
183 EndpointDiscovery &ed = config.interfaces[InterfaceNumber].endpoints;
184 if( !ed.IsValid() || ed.GetEndpointPairs().size() == 0 ) {
185 dout("Probe: endpoint invalid. ed.IsValud() == "
186 << (ed.IsValid() ? "true" : "false")
187 << ", ed.GetEndpointPairs().size() == "
188 << ed.GetEndpointPairs().size());
189 return;
192 ProbeResult result;
193 result.m_dev = devid;
194 result.m_interface = InterfaceNumber;
195 result.m_zeroSocketSequence = 0;
197 // find the first bulk read/write endpoint pair that answers
198 // to our probe commands
199 // Start with second pair, since evidence indicates the later pairs
200 // are the ones we need.
201 for(size_t i = ed.GetEndpointPairs().size() > 1 ? 1 : 0;
202 i < ed.GetEndpointPairs().size();
203 i++ )
205 const EndpointPair &ep = ed.GetEndpointPairs()[i];
206 if( ep.type == USB_ENDPOINT_TYPE_BULK ) {
207 result.m_ep = ep;
209 Device dev(devid);
210 // dev.Reset();
211 // sleep(5);
213 unsigned char cfg;
214 if( !dev.GetConfiguration(cfg) )
215 throw Usb::Error(dev.GetLastError(),
216 "Probe: GetConfiguration failed");
217 if( cfg != BLACKBERRY_CONFIGURATION ) {
218 if( !dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
219 throw Usb::Error(dev.GetLastError(),
220 "Probe: SetConfiguration failed");
223 Interface iface(dev, InterfaceNumber);
225 dev.ClearHalt(ep.read);
226 dev.ClearHalt(ep.write);
228 Data data;
229 dev.BulkDrain(ep.read);
230 if( !Intro(0, ep, dev, data) ) {
231 dout("Probe: Intro(0) failed");
232 continue;
235 Socket socket(dev, ep.write, ep.read);
237 Data send, receive;
238 ZeroPacket packet(send, receive);
240 // unknown attribute: 0x14 / 0x01
241 packet.GetAttribute(SB_OBJECT_INITIAL_UNKNOWN,
242 SB_ATTR_INITIAL_UNKNOWN);
243 socket.Send(packet);
245 // fetch PIN
246 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_PIN);
247 socket.Send(packet);
248 if( packet.ObjectID() != SB_OBJECT_PROFILE ||
249 packet.AttributeID() != SB_ATTR_PROFILE_PIN ||
250 !ParsePIN(receive, result) )
252 dout("Probe: unable to fetch PIN");
253 continue;
256 // fetch Description
257 packet.GetAttribute(SB_OBJECT_PROFILE, SB_ATTR_PROFILE_DESC);
258 socket.Send(packet);
259 // response ObjectID does not match request... :-/
260 if( // packet.ObjectID() != SB_OBJECT_PROFILE ||
261 packet.AttributeID() != SB_ATTR_PROFILE_DESC ||
262 !ParseDesc(receive, result) )
264 dout("Probe: unable to fetch description");
265 // this is a relatively new feature, so don't
266 // fail here... just blank the description
267 result.m_description.clear();
270 // more unknowns:
271 for( uint16_t attr = 5; attr < 9; attr++ ) {
272 packet.GetAttribute(SB_OBJECT_SOCKET_UNKNOWN, attr);
273 socket.Send(packet);
274 // FIXME parse these responses, if they turn
275 // out to be important
279 // all info obtained, add to list
280 result.m_zeroSocketSequence = socket.GetZeroSocketSequence();
281 m_results.push_back(result);
282 ddout("Using ReadEndpoint: " << (unsigned int)result.m_ep.read);
283 ddout(" WriteEndpoint: " << (unsigned int)result.m_ep.write);
284 break;
286 else {
287 dout("Probe: Skipping non-bulk endpoint pair (offset: "
288 << i-1 << ") ");
292 if( !result.m_ep.IsComplete() )
293 ddout("Unable to discover endpoint pair for one device.");
296 int Probe::FindActive(uint32_t pin) const
298 for( int i = 0; i < GetCount(); i++ ) {
299 if( Get(i).m_pin == pin )
300 return i;
302 if( pin == 0 ) {
303 // can we default to a single device?
304 if( GetCount() == 1 )
305 return 0; // yes!
308 // PIN not found
309 return -1;
312 std::ostream& operator<< (std::ostream &os, const ProbeResult &pr)
314 os << "Device ID: " << pr.m_dev
315 << std::hex << ". PIN: " << pr.m_pin
316 << ", Description: " << pr.m_description;
317 return os;
320 } // namespace Barry