3 /// Mode class for GPRS modem mode (using endpoints on
8 Copyright (C) 2008-2010, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "m_ipmodem.h"
24 #include "controller.h"
31 namespace Barry
{ namespace Mode
{
33 const char special_flag
[] = { 0x78, 0x56, 0x34, 0x12 }; // 0x12345678
34 const char start
[] = { 0x01, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
35 const char pw_start
[] = { 0x01, 0, 0, 0, 1, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
36 const char stop
[] = { 0x01, 0, 0, 0, 0, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
38 //////////////////////////////////////////////////////////////////////////////
39 // Mode::IpModem class
41 IpModem::IpModem(Controller
&con
,
42 DeviceDataCallback callback
,
43 void *callback_context
)
46 , m_continue_reading(false)
47 , m_callback(callback
)
48 , m_callback_context(callback_context
)
50 memset(m_session_key
, 0, sizeof(m_session_key
));
57 } catch( std::exception
&e
) {
58 dout("Exception caught in IpModem destructor, ignoring: "
63 bool IpModem::SendPassword( const char *password
, uint32_t seed
)
65 if( !password
|| strlen(password
) == 0 ) {
66 throw BadPassword("Logic error: No password provided in SendPassword.", 0, false);
69 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
70 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
71 unsigned char pwdigest
[SHA_DIGEST_LENGTH
];
72 unsigned char prefixedhash
[SHA_DIGEST_LENGTH
+ 4];
73 unsigned char pw_response
[SHA_DIGEST_LENGTH
+ 8];
77 if( !password
|| strlen(password
) == 0 ) {
78 throw BadPassword("No password provided.", 0, false);
81 // Build the password hash
82 // first, hash the password by itself
83 SHA1((unsigned char *) password
, strlen(password
), pwdigest
);
85 // prefix the resulting hash with the provided seed
86 memcpy(&prefixedhash
[0], &seed
, sizeof(uint32_t));
87 memcpy(&prefixedhash
[4], pwdigest
, SHA_DIGEST_LENGTH
);
90 SHA1((unsigned char *) prefixedhash
, SHA_DIGEST_LENGTH
+ 4, pwdigest
);
92 // Build the response packet
93 const char pw_rsphdr
[] = { 0x03, 0x00, 0x00, 0x00 };
94 memcpy(&pw_response
[0], pw_rsphdr
, sizeof(pw_rsphdr
));
95 memcpy(&pw_response
[4], pwdigest
, SHA_DIGEST_LENGTH
);
96 memcpy(&pw_response
[24], special_flag
, sizeof(special_flag
));
98 // Send the password response packet
99 m_dev
.BulkWrite(write_ep
, pw_response
, sizeof(pw_response
));
100 m_dev
.BulkRead(read_ep
, data
);
101 ddout("IPModem: Read password response.\n" << data
);
103 // Added for the BB Storm 9000's second password request
104 if( data
.GetSize() >= 16 && data
.GetData()[0] == 0x00 ) {
106 m_dev
.BulkRead(read_ep
, data
, 500);
107 ddout("IPModem: Null Response Packet:\n" << data
);
109 catch( Usb::Timeout
&to
) {
110 // do nothing on timeouts
111 ddout("IPModem: Null Response Timeout");
116 // check response 04 00 00 00 .......
117 // On the 8703e the seed is incremented, retries are reset to 10
118 // when the password is accepted.
120 // If data.GetData() + 4 is = to the orginal seed +1 or 00 00 00 00
121 // then the password was acceppted.
123 // When data.GetData() + 4 is not 00 00 00 00 then data.GetData()[8]
124 // contains the number of password retrys left.
126 if( data
.GetSize() >= 9 && data
.GetData()[0] == 0x04 ) {
127 memcpy(&new_seed
, data
.GetData() + 4, sizeof(uint32_t));
129 if( seed
== new_seed
|| new_seed
== 0 ) {
130 ddout("IPModem: Password accepted.\n");
132 #if SHA_DIGEST_LENGTH < SB_IPMODEM_SESSION_KEY_LENGTH
133 #error Session key field must be smaller than SHA digest
135 // Create session key - last 8 bytes of the password hash
136 memcpy(&m_session_key
[0],
137 pwdigest
+ SHA_DIGEST_LENGTH
- sizeof(m_session_key
),
138 sizeof(m_session_key
));
140 // blank password hashes as we don't need these anymore
141 memset(pwdigest
, 0, sizeof(pwdigest
));
142 memset(prefixedhash
, 0, sizeof(prefixedhash
));
146 ddout("IPModem: Invalid password.\n" << data
);
147 throw BadPassword("Password rejected by device.", data
.GetData()[8], false);
151 ddout("IPModem: Error unknown packet.\n" << data
);
155 //////////////////////////////////////////////////////////////////////////////
156 // protected API / static functions
158 void *IpModem::DataReadThread(void *userptr
)
160 IpModem
*ipmodem
= (IpModem
*) userptr
;
162 int read_ep
= ipmodem
->m_con
.GetProbeResult().m_epModem
.read
;
165 while( ipmodem
->m_continue_reading
) {
169 ipmodem
->m_dev
.BulkRead(read_ep
, data
, 5000);
171 // is it a special code?
172 if( data
.GetSize() > 4 &&
173 memcmp(data
.GetData() + data
.GetSize() - 4, special_flag
, sizeof(special_flag
)) == 0 ) {
174 // log, then drop it on the floor for now
175 ddout("IPModem: Special packet:\n" << data
);
179 // call callback if available
180 if( ipmodem
->m_callback
) {
181 (*ipmodem
->m_callback
)(ipmodem
->m_callback_context
,
186 // // append data to readCache
191 catch( Usb::Timeout
&to
) {
192 // do nothing on timeouts
193 ddout("IPModem: Timeout in DataReadThread!");
195 catch( std::exception
&e
) {
196 eout("Exception in IpModem::DataReadThread: " << e
.what());
203 //////////////////////////////////////////////////////////////////////////////
206 void IpModem::Open(const char *password
)
208 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
209 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
210 unsigned char response
[28];
214 // check that we have endpoints for the modem
215 const Usb::EndpointPair
&pair
= m_con
.GetProbeResult().m_epModem
;
216 if( !pair
.IsComplete() ) {
217 std::ostringstream oss
;
218 oss
<< "IP Modem not supported by this device: "
219 << "read: " << std::hex
<< (unsigned int) pair
.read
220 << " write: " << std::hex
<< (unsigned int) pair
.write
221 << " type: " << std::hex
<< (unsigned int) pair
.type
;
223 throw Barry::Error(oss
.str());
226 // clear halt when starting out only if needed
227 if( m_con
.m_result
.m_needClearHalt
) {
228 m_dev
.ClearHalt(pair
.read
);
229 m_dev
.ClearHalt(pair
.write
);
233 ddout("IPModem: Sending Stop Response:\n");
234 m_dev
.BulkWrite(write_ep
, stop
, sizeof(stop
));
236 m_dev
.BulkRead(read_ep
, data
, 500);
237 ddout("IPModem: Stop Response Packet:\n" << data
);
239 catch( Usb::Timeout
&to
) {
240 // do nothing on timeouts
241 ddout("IPModem: Stop Response Timeout");
244 // Send start commands to figure out if the device needs a password.
245 ddout("IPModem: Sending Start Response:\n");
246 m_dev
.BulkWrite(write_ep
, pw_start
, sizeof(pw_start
));
247 m_dev
.BulkRead(read_ep
, data
, 5000);
248 ddout("IPModem: Start Response Packet:\n" << data
);
250 // check for 02 00 00 00 SS SS SS SS RR 00 00 00 0a 00 00 00 PP PP PP PP PP 00 00 00 78 56 34 12
251 if( data
.GetSize() >= 9 && data
.GetData()[0] == 0x02 &&
252 memcmp(data
.GetData() + data
.GetSize() - 4, special_flag
, sizeof(special_flag
))== 0 ) {
253 // Got a password request packet
254 ddout("IPModem: Password request packet:\n" << data
);
256 // Check how many retries are left
257 if( data
.GetData()[8] < BARRY_MIN_PASSWORD_TRIES
) {
258 throw BadPassword("Fewer than " BARRY_MIN_PASSWORD_TRIES_ASC
" password tries remaining in device. Refusing to proceed, to avoid device zapping itself. Use a Windows client, or re-cradle the device.",
262 memcpy(&seed
, data
.GetData() + 4, sizeof(seed
));
264 if( !SendPassword(password
, seed
) ) {
265 throw Barry::Error("IpModem: Error sending password.");
268 // Re-send "start" packet
269 ddout("IPModem: Re-sending Start Response:\n");
270 m_dev
.BulkWrite(write_ep
, pw_start
, sizeof(pw_start
));
271 m_dev
.BulkRead(read_ep
, data
);
272 ddout("IPModem: Start Response Packet:\n" << data
);
275 // send packet with the session_key
276 unsigned char response_header
[] = { 0x00, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0xc2, 1, 0 };
277 memcpy(&response
[0], response_header
, sizeof(response_header
));
278 memcpy(&response
[16], m_session_key
, sizeof(m_session_key
));
279 memcpy(&response
[24], special_flag
, sizeof(special_flag
));
280 ddout("IPModem: Sending Session key:\n");
281 m_dev
.BulkWrite(write_ep
, response
, sizeof(response
));
282 if( data
.GetSize() >= 16 ) {
283 switch(data
.GetData()[0])
285 case 0x00: // Null packet
288 case 0x02: // password seed received
289 memcpy(&seed
, data
.GetData() + 4, sizeof(uint32_t));
290 if( !SendPassword( password
, seed
) ) {
291 throw Barry::Error("IpModem: Error sending password.");
294 case 0x04: // command accepted
298 ddout("IPModem: Unknown response.\n");
303 // see if the modem will respond to commands
304 const char modem_command
[] = { "AT\r" };
305 m_dev
.BulkWrite(write_ep
, modem_command
, strlen(modem_command
));
306 m_dev
.BulkRead(read_ep
, data
);
307 ddout("IPModem: Test command response.\n" << data
);
308 if( data
.GetSize() >= 1 ) {
309 switch(data
.GetData()[0])
311 case 0x00: // Null packet
313 m_dev
.BulkRead(read_ep
, data
, 5000);
314 ddout("IPModem: AT Response Packet:\n" << data
);
316 catch( Usb::Timeout
&to
) {
317 // do nothing on timeouts
318 ddout("IPModem: AT Response Timeout");
322 case 0x02: // password seed received
323 if( !password
|| strlen(password
) == 0 ) {
324 throw BadPassword("This device requested a password.",
325 data
.GetSize() >= 9 ? data
.GetData()[8] : 0, false);
327 else { // added for the Storm 9000
328 memcpy(&seed
, data
.GetData() + 4, sizeof(seed
));
329 if( !SendPassword( password
, seed
) ) {
330 throw Barry::Error("IpModem: Error sending password.");
334 case 0x04: // command accepted
337 case 0x07: // device is password protected?
338 throw BadPassword("This device requires a password.", 0, false);
341 ddout("IPModem: Unknown AT command response.\n");
342 // treat this unknown data as a serial response
344 (*m_callback
)(m_callback_context
,
351 ddout("IPModem: Modem Ready.\n");
354 m_continue_reading
= true;
355 int ret
= pthread_create(&m_modem_read_thread
, NULL
, &IpModem::DataReadThread
, this);
357 m_continue_reading
= false;
358 throw Barry::ErrnoError("IpModem: Error creating USB read thread.", ret
);
362 void IpModem::Write(const Data
&data
, int timeout
)
364 if( data
.GetSize() == 0 )
365 return; // nothing to do
367 // according to Rick Scott the m_filter is not needed with the ip modem
368 // but with the 8320 with Rogers, it doesn't seem to connect without it
369 // If this is a performance problem, perhaps make this a runtime
371 m_dev
.BulkWrite(m_con
.GetProbeResult().m_epModem
.write
,
372 m_filter
.Write(data
), timeout
);
375 void IpModem::Close()
377 // This is the terminate connection sequence
378 // that resets the modem so we can re-connect
379 // without unpluging the USB cable or reseting
381 // This works on a BB 8703e a with password. other BB's??
382 unsigned char end
[28];
383 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
384 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
387 //0 0 0 0 b0 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
388 ddout("IpModem: Closing connection.");
389 memset(end
, 0, sizeof(end
));
393 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
394 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
395 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
397 //0 0 0 0 20 0 0 0 3 0 0 0 0 c2 1 0 + session_key + special_flag
398 memset(end
, 0, sizeof(end
));
403 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
404 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
405 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
407 //0 0 0 0 30 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
408 // The session_key is set to 0x0's when there is no password.
409 memset(end
, 0, sizeof(end
));
413 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
414 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
415 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
416 m_dev
.BulkWrite(write_ep
, stop
, sizeof(stop
));
418 m_dev
.BulkRead(read_ep
, data
, 5000);
419 ddout("IPModem: Close read packet:\n" << data
);
421 catch( Usb::Timeout
&to
) {
422 // do nothing on timeouts
423 ddout("IPModem: Close Read Timeout");
425 // stop the read thread
426 if( m_continue_reading
) {
427 m_continue_reading
= false;
428 pthread_join(m_modem_read_thread
, NULL
);
430 ddout("IPmodem: Closed!");
434 }} // namespace Barry::Mode