3 /// Mode class for GPRS modem mode (using endpoints on
8 Copyright (C) 2008-2012, 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"
25 #include "controllerpriv.h"
32 namespace Barry
{ namespace Mode
{
34 const char special_flag
[] = { 0x78, 0x56, 0x34, 0x12 }; // 0x12345678
35 const char start
[] = { 0x01, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
36 const char pw_start
[] = { 0x01, 0, 0, 0, 1, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
37 const char stop
[] = { 0x01, 0, 0, 0, 0, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
39 //////////////////////////////////////////////////////////////////////////////
40 // Mode::IpModem class
42 IpModem::IpModem(Controller
&con
,
43 DeviceDataCallback callback
,
44 void *callback_context
)
46 , m_dev(con
.GetPrivate()->m_dev
)
47 , m_continue_reading(false)
48 , m_callback(callback
)
49 , m_callback_context(callback_context
)
51 memset(m_session_key
, 0, sizeof(m_session_key
));
58 } catch( std::exception
&e
) {
59 dout("Exception caught in IpModem destructor, ignoring: "
64 bool IpModem::SendPassword( const char *password
, uint32_t seed
)
66 if( !password
|| strlen(password
) == 0 ) {
67 throw BadPassword("Logic error: No password provided in SendPassword.", 0, false);
70 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
71 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
72 unsigned char pwdigest
[SHA_DIGEST_LENGTH
];
73 unsigned char prefixedhash
[SHA_DIGEST_LENGTH
+ 4];
74 unsigned char pw_response
[SHA_DIGEST_LENGTH
+ 8];
78 if( !password
|| strlen(password
) == 0 ) {
79 throw BadPassword("No password provided.", 0, false);
82 // Build the password hash
83 // first, hash the password by itself
84 SHA1((unsigned char *) password
, strlen(password
), pwdigest
);
86 // prefix the resulting hash with the provided seed
87 memcpy(&prefixedhash
[0], &seed
, sizeof(uint32_t));
88 memcpy(&prefixedhash
[4], pwdigest
, SHA_DIGEST_LENGTH
);
91 SHA1((unsigned char *) prefixedhash
, SHA_DIGEST_LENGTH
+ 4, pwdigest
);
93 // Build the response packet
94 const char pw_rsphdr
[] = { 0x03, 0x00, 0x00, 0x00 };
95 memcpy(&pw_response
[0], pw_rsphdr
, sizeof(pw_rsphdr
));
96 memcpy(&pw_response
[4], pwdigest
, SHA_DIGEST_LENGTH
);
97 memcpy(&pw_response
[24], special_flag
, sizeof(special_flag
));
99 // Send the password response packet
100 m_dev
.BulkWrite(write_ep
, pw_response
, sizeof(pw_response
));
101 m_dev
.BulkRead(read_ep
, data
);
102 ddout("IPModem: Read password response.\n" << data
);
104 // Added for the BB Storm 9000's second password request
105 if( data
.GetSize() >= 16 && data
.GetData()[0] == 0x00 ) {
107 m_dev
.BulkRead(read_ep
, data
, 500);
108 ddout("IPModem: Null Response Packet:\n" << data
);
110 catch( Usb::Timeout
&to
) {
111 // do nothing on timeouts
112 ddout("IPModem: Null Response Timeout");
117 // check response 04 00 00 00 .......
118 // On the 8703e the seed is incremented, retries are reset to 10
119 // when the password is accepted.
121 // If data.GetData() + 4 is = to the orginal seed +1 or 00 00 00 00
122 // then the password was acceppted.
124 // When data.GetData() + 4 is not 00 00 00 00 then data.GetData()[8]
125 // contains the number of password retrys left.
127 if( data
.GetSize() >= 9 && data
.GetData()[0] == 0x04 ) {
128 memcpy(&new_seed
, data
.GetData() + 4, sizeof(uint32_t));
130 if( seed
== new_seed
|| new_seed
== 0 ) {
131 ddout("IPModem: Password accepted.\n");
133 #if SHA_DIGEST_LENGTH < SB_IPMODEM_SESSION_KEY_LENGTH
134 #error Session key field must be smaller than SHA digest
136 // Create session key - last 8 bytes of the password hash
137 memcpy(&m_session_key
[0],
138 pwdigest
+ SHA_DIGEST_LENGTH
- sizeof(m_session_key
),
139 sizeof(m_session_key
));
141 // blank password hashes as we don't need these anymore
142 memset(pwdigest
, 0, sizeof(pwdigest
));
143 memset(prefixedhash
, 0, sizeof(prefixedhash
));
147 ddout("IPModem: Invalid password.\n" << data
);
148 throw BadPassword("Password rejected by device.", data
.GetData()[8], false);
152 ddout("IPModem: Error unknown packet.\n" << data
);
156 //////////////////////////////////////////////////////////////////////////////
157 // protected API / static functions
159 void *IpModem::DataReadThread(void *userptr
)
161 IpModem
*ipmodem
= (IpModem
*) userptr
;
163 int read_ep
= ipmodem
->m_con
.GetProbeResult().m_epModem
.read
;
166 while( ipmodem
->m_continue_reading
) {
170 ipmodem
->m_dev
.BulkRead(read_ep
, data
, 5000);
172 // is it a special code?
173 if( data
.GetSize() > 4 &&
174 memcmp(data
.GetData() + data
.GetSize() - 4, special_flag
, sizeof(special_flag
)) == 0 ) {
175 // log, then drop it on the floor for now
176 ddout("IPModem: Special packet:\n" << data
);
180 // call callback if available
181 if( ipmodem
->m_callback
) {
182 (*ipmodem
->m_callback
)(ipmodem
->m_callback_context
,
187 // // append data to readCache
192 catch( Usb::Timeout
&to
) {
193 // do nothing on timeouts
194 ddout("IPModem: Timeout in DataReadThread!");
196 catch( std::exception
&e
) {
197 eout("Exception in IpModem::DataReadThread: " << e
.what());
204 //////////////////////////////////////////////////////////////////////////////
207 void IpModem::Open(const char *password
)
209 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
210 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
211 unsigned char response
[28];
215 // check that we have endpoints for the modem
216 const Usb::EndpointPair
&pair
= m_con
.GetProbeResult().m_epModem
;
217 if( !pair
.IsComplete() ) {
218 std::ostringstream oss
;
219 oss
<< "IP Modem not supported by this device: "
220 << "read: " << std::hex
<< (unsigned int) pair
.read
221 << " write: " << std::hex
<< (unsigned int) pair
.write
222 << " type: " << std::hex
<< (unsigned int) pair
.type
;
224 throw Barry::Error(oss
.str());
227 // clear halt when starting out only if needed
228 if( m_con
.GetProbeResult().m_needClearHalt
) {
229 m_dev
.ClearHalt(pair
.read
);
230 m_dev
.ClearHalt(pair
.write
);
234 ddout("IPModem: Sending Stop Response:\n");
235 m_dev
.BulkWrite(write_ep
, stop
, sizeof(stop
));
237 m_dev
.BulkRead(read_ep
, data
, 500);
238 ddout("IPModem: Stop Response Packet:\n" << data
);
240 catch( Usb::Timeout
&to
) {
241 // do nothing on timeouts
242 ddout("IPModem: Stop Response Timeout");
245 // Send start commands to figure out if the device needs a password.
246 ddout("IPModem: Sending Start Response:\n");
247 m_dev
.BulkWrite(write_ep
, pw_start
, sizeof(pw_start
));
248 m_dev
.BulkRead(read_ep
, data
, 5000);
249 ddout("IPModem: Start Response Packet:\n" << data
);
251 // 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
252 if( data
.GetSize() >= 9 && data
.GetData()[0] == 0x02 &&
253 memcmp(data
.GetData() + data
.GetSize() - 4, special_flag
, sizeof(special_flag
))== 0 ) {
254 // Got a password request packet
255 ddout("IPModem: Password request packet:\n" << data
);
257 // Check how many retries are left
258 if( data
.GetData()[8] < BARRY_MIN_PASSWORD_TRIES
) {
259 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.",
263 memcpy(&seed
, data
.GetData() + 4, sizeof(seed
));
265 if( !SendPassword(password
, seed
) ) {
266 throw Barry::Error("IpModem: Error sending password.");
269 // Re-send "start" packet
270 ddout("IPModem: Re-sending Start Response:\n");
271 m_dev
.BulkWrite(write_ep
, pw_start
, sizeof(pw_start
));
272 m_dev
.BulkRead(read_ep
, data
);
273 ddout("IPModem: Start Response Packet:\n" << data
);
276 // send packet with the session_key
277 unsigned char response_header
[] = { 0x00, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0xc2, 1, 0 };
278 memcpy(&response
[0], response_header
, sizeof(response_header
));
279 memcpy(&response
[16], m_session_key
, sizeof(m_session_key
));
280 memcpy(&response
[24], special_flag
, sizeof(special_flag
));
281 ddout("IPModem: Sending Session key:\n");
282 m_dev
.BulkWrite(write_ep
, response
, sizeof(response
));
283 if( data
.GetSize() >= 16 ) {
284 switch(data
.GetData()[0])
286 case 0x00: // Null packet
289 case 0x02: // password seed received
290 memcpy(&seed
, data
.GetData() + 4, sizeof(uint32_t));
291 if( !SendPassword( password
, seed
) ) {
292 throw Barry::Error("IpModem: Error sending password.");
295 case 0x04: // command accepted
299 ddout("IPModem: Unknown response.\n");
304 // see if the modem will respond to commands
305 const char modem_command
[] = { "AT\r" };
306 m_dev
.BulkWrite(write_ep
, modem_command
, strlen(modem_command
));
307 m_dev
.BulkRead(read_ep
, data
);
308 ddout("IPModem: Test command response.\n" << data
);
309 if( data
.GetSize() >= 1 ) {
310 switch(data
.GetData()[0])
312 case 0x00: // Null packet
314 m_dev
.BulkRead(read_ep
, data
, 5000);
315 ddout("IPModem: AT Response Packet:\n" << data
);
317 catch( Usb::Timeout
&to
) {
318 // do nothing on timeouts
319 ddout("IPModem: AT Response Timeout");
323 case 0x02: // password seed received
324 if( !password
|| strlen(password
) == 0 ) {
325 throw BadPassword("This device requested a password.",
326 data
.GetSize() >= 9 ? data
.GetData()[8] : 0, false);
328 else { // added for the Storm 9000
329 memcpy(&seed
, data
.GetData() + 4, sizeof(seed
));
330 if( !SendPassword( password
, seed
) ) {
331 throw Barry::Error("IpModem: Error sending password.");
335 case 0x04: // command accepted
338 case 0x07: // device is password protected?
339 throw BadPassword("This device requires a password.", 0, false);
342 ddout("IPModem: Unknown AT command response.\n");
343 // treat this unknown data as a serial response
345 (*m_callback
)(m_callback_context
,
352 ddout("IPModem: Modem Ready.\n");
355 m_continue_reading
= true;
356 int ret
= pthread_create(&m_modem_read_thread
, NULL
, &IpModem::DataReadThread
, this);
358 m_continue_reading
= false;
359 throw Barry::ErrnoError("IpModem: Error creating USB read thread.", ret
);
363 void IpModem::Write(const Data
&data
, int timeout
)
365 if( data
.GetSize() == 0 )
366 return; // nothing to do
368 // according to Rick Scott the m_filter is not needed with the ip modem
369 // but with the 8320 with Rogers, it doesn't seem to connect without it
370 // If this is a performance problem, perhaps make this a runtime
372 m_dev
.BulkWrite(m_con
.GetProbeResult().m_epModem
.write
,
373 m_filter
.Write(data
), timeout
);
376 void IpModem::Close()
378 // This is the terminate connection sequence
379 // that resets the modem so we can re-connect
380 // without unpluging the USB cable or reseting
382 // This works on a BB 8703e a with password. other BB's??
383 unsigned char end
[28];
384 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
385 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
388 //0 0 0 0 b0 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
389 ddout("IpModem: Closing connection.");
390 memset(end
, 0, sizeof(end
));
394 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
395 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
396 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
398 //0 0 0 0 20 0 0 0 3 0 0 0 0 c2 1 0 + session_key + special_flag
399 memset(end
, 0, sizeof(end
));
404 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
405 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
406 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
408 //0 0 0 0 30 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
409 // The session_key is set to 0x0's when there is no password.
410 memset(end
, 0, sizeof(end
));
414 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
415 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
416 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
417 m_dev
.BulkWrite(write_ep
, stop
, sizeof(stop
));
419 // stop the read thread
420 if( m_continue_reading
) {
421 m_continue_reading
= false;
422 pthread_join(m_modem_read_thread
, NULL
);
425 // otherwise, drain the last read
427 m_dev
.BulkRead(read_ep
, data
, 5000);
428 ddout("IPModem: Close read packet:\n" << data
);
430 catch( Usb::Timeout
&to
) {
431 // do nothing on timeouts
432 ddout("IPModem: Close Read Timeout");
436 ddout("IPmodem: Closed!");
440 }} // namespace Barry::Mode