]> git.gir.st - tmk_keyboard.git/blob - tool/mbed/mbed-sdk/libraries/net/cellular/UbloxUSBModem/UbloxUSBGSMModem.cpp
Squashed 'tmk_core/' changes from 7967731..b9e0ea0
[tmk_keyboard.git] / tool / mbed / mbed-sdk / libraries / net / cellular / UbloxUSBModem / UbloxUSBGSMModem.cpp
1 /* UbloxUSBGSMModem.cpp */
2 /* Copyright (C) 2012 mbed.org, MIT License
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
6 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
7 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in all copies or
11 * substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 */
19
20 #define __DEBUG__ 3
21 #ifndef __MODULE__
22 #define __MODULE__ "UbloxUSBGSMModem.cpp"
23 #endif
24
25 #include "core/fwk.h"
26
27 #include "UbloxUSBGSMModem.h"
28 #include "UbloxGSMModemInitializer.h"
29 #include "USBHost.h"
30
31 UbloxUSBGSMModem::UbloxUSBGSMModem(PinName powerGatingPin /*= NC*/, bool powerGatingOnWhenPinHigh /* = true*/) :
32 m_dongle(), // Construct WANDongle: USB interface with two serial channels to the modem (USBSerialStream objects)
33 m_atStream(m_dongle.getSerial(1)), // AT commands are sent down one serial channel.
34 m_pppStream(m_dongle.getSerial(0)), // PPP connections are managed via another serial channel.
35 m_at(&m_atStream), // Construct ATCommandsInterface with the AT serial channel
36 m_sms(&m_at), // Construct SMSInterface with the ATCommandsInterface
37 m_ussd(&m_at), // Construct USSDInterface with the ATCommandsInterface
38 m_linkMonitor(&m_at), // Construct LinkMonitor with the ATCommandsInterface
39 m_ppp(&m_pppStream), // Construct PPPIPInterface with the PPP serial channel
40 m_dongleConnected(false), // Dongle is initially not ready for anything
41 m_ipInit(false), // PPIPInterface connection is initially down
42 m_smsInit(false), // SMSInterface starts un-initialised
43 m_ussdInit(false), // USSDInterface starts un-initialised
44 m_linkMonitorInit(false), // LinkMonitor subsystem starts un-initialised
45 m_atOpen(false), // ATCommandsInterface starts in a closed state
46 m_powerGatingPin(powerGatingPin), // set power gating pin
47 m_powerGatingOnWhenPinHigh(powerGatingOnWhenPinHigh) // set state semantics for power gating pin
48 {
49 USBHost* host = USBHost::getHostInst();
50 m_dongle.addInitializer(new UbloxGSMModemInitializer(host));
51 if( m_powerGatingPin != NC )
52 {
53 power(false); //Dongle will have to be powered on manually
54 }
55 }
56
57 class CREGProcessor : public IATCommandsProcessor
58 {
59 public:
60 CREGProcessor() : status(STATUS_REGISTERING)
61 {
62
63 }
64 enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
65 REGISTERING_STATUS getStatus()
66 {
67 return status;
68 }
69 private:
70 virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
71 {
72 int r;
73 if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
74 {
75 switch(r)
76 {
77 case 1:
78 case 5:
79 status = STATUS_OK;
80 break;
81 case 0:
82 case 2:
83 status = STATUS_REGISTERING;
84 break;
85 case 3:
86 default:
87 status = STATUS_FAILED;
88 break;
89 }
90 }
91 return OK;
92 }
93 virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
94 {
95 return OK;
96 }
97 volatile REGISTERING_STATUS status;
98 };
99
100 #if 0
101 class COPSProcessor : public IATCommandsProcessor
102 {
103 public:
104 COPSProcessor() : valid(false)
105 {
106 network[0] = '\0';
107 apn[0] = '\0';
108 bearer[0] = '\0';
109 }
110 char* getNetwork()
111 {
112 return network;
113 }
114 char* getAPN()
115 {
116 return apn;
117 }
118 char* getBearer()
119 {
120 return bearer;
121 }
122 bool isValid()
123 {
124 return valid;
125 }
126 private:
127 virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
128 {
129 int networkId;
130 int bearerId;
131 int s = sscanf(line, "+COPS: %*d,%*d,\"%d\",%d", &networkId, &bearerId);
132 if( s == 2 )
133 {
134 switch(networkId)
135 {
136 case 23415:
137 strcpy(network, "Vodafone UK");
138 strcpy(apn, "pp.vodafone.co.uk");
139 valid = true;
140 break;
141 case 20810:
142 strcpy(network, "SFR FR");
143 strcpy(apn, "websfr");
144 valid = true;
145 break;
146 default:
147 break;
148 }
149 }
150 else
151 {
152 return OK;
153 }
154 switch(bearerId)
155 {
156 case 0: strcpy(bearer, "GSM"); break;
157 case 1: strcpy(bearer, "GSM Compact"); break;
158 case 2: strcpy(bearer, "UTRAN"); break;
159 case 3: strcpy(bearer, "GSM w/EGPRS"); break;
160 case 4: strcpy(bearer, "UTRAN w/HSDPA"); break;
161 case 5: strcpy(bearer, "UTRAN w/HSUPA"); break;
162 case 6: strcpy(bearer, "UTRAN w/HSDPA and HSUPA"); break;
163 case 7: strcpy(bearer, "E-UTRAN"); break;
164
165 default:
166 break;
167 }
168 return OK;
169 }
170 virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
171 {
172 return OK;
173 }
174 char network[24];
175 char bearer[24];
176 char apn[24];
177 volatile bool valid;
178 };
179 #endif
180
181 int UbloxUSBGSMModem::connect(const char* apn, const char* user, const char* password)
182 {
183 if( !m_ipInit )
184 {
185 m_ipInit = true;
186 m_ppp.init();
187 }
188 m_ppp.setup(user, password, DEFAULT_MSISDN_GSM);
189
190 int ret = init();
191 if(ret)
192 {
193 return ret;
194 }
195
196 #if USE_ONE_PORT
197 m_smsInit = false; //SMS status reset
198 m_ussdInit = false; //USSD status reset
199 m_linkMonitorInit = false; //Link monitor status reset
200 #endif
201
202 ATCommandsInterface::ATResult result;
203
204 #if 0
205 //Get network info & select corresponding APN
206 COPSProcessor copsProcessor;
207 DBG("Get network info & select APN from DB");
208 ret = m_at.execute("AT+COPS=,2;+COPS?", &copsProcessor, &result); //Configure to get operator's info in numeric code & get operator's id
209 DBG("Result of command: Err code=%d", ret);
210 DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
211
212 if(!copsProcessor.isValid())
213 {
214 WARN("Connected to an unknown network, try to connect with default parameters");
215 DBG("Connected with %s", copsProcessor.getBearer());
216 }
217 else
218 {
219 DBG("Connected to %s with %s", copsProcessor.getNetwork(), copsProcessor.getBearer());
220 char cmd[48];
221 int tries = 3;
222 sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", copsProcessor.getAPN());
223 do //Try 3 times because for some reasons it can fail with the K3772-Z dongle
224 {
225 ret = m_at.executeSimple(cmd, &result);
226 DBG("Result of command: Err code=%d", ret);
227 } while(ret && --tries);
228 DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
229 DBG("APN set to %s", copsProcessor.getAPN());
230 }
231 #else
232 if(apn != NULL)
233 {
234 char cmd[48];
235 int tries = 30;
236 sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
237 do //Try 30 times because for some reasons it can fail *a lot* with the K3772-Z dongle
238 {
239 ret = m_at.executeSimple(cmd, &result);
240 DBG("Result of command: Err code=%d", ret);
241 if(ret)
242 {
243 Thread::wait(500);
244 }
245 } while(ret && --tries);
246 DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
247 DBG("APN set to %s", apn);
248 }
249 #endif
250
251
252 //Connect
253 DBG("Connecting");
254 #if 0
255 ret = m_at.executeSimple("ATDT *99#", &result);
256 DBG("Result of command: Err code=%d", ret);
257 DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
258 #endif
259 #if USE_ONE_PORT
260 m_at.close(); // Closing AT parser
261 m_atOpen = false; //Will need to be reinitialized afterwards
262 #endif
263
264 #if 0
265 DBG("AT Parser closed");
266 if( (ret!=NET_MOREINFO) || (result.result != ATCommandsInterface::ATResult::AT_CONNECT))
267 {
268 ERR("Could not connect");
269 return ret; //Could not connect
270 }
271 #endif
272 DBG("Connecting PPP");
273
274 ret = m_ppp.connect();
275 DBG("Result of connect: Err code=%d", ret);
276 return ret;
277 }
278
279
280 int UbloxUSBGSMModem::disconnect()
281 {
282 DBG("Disconnecting from PPP");
283 int ret = m_ppp.disconnect();
284 if(ret)
285 {
286 ERR("Disconnect returned %d, still trying to disconnect", ret);
287 }
288
289 //Ugly but leave dongle time to recover
290 Thread::wait(500);
291
292 #if USE_ONE_PORT
293 ATCommandsInterface::ATResult result;
294 DBG("Starting AT thread");
295 ret = m_at.open();
296 if(ret)
297 {
298 return ret;
299 }
300 #endif
301
302 DBG("Trying to hangup");
303
304 #if 0 //Does not appear to work
305 int tries = 10;
306 do
307 {
308 ret = m_at.executeSimple("+++", &result, 1000);
309 DBG("Result of command: Err code=%d\n", ret);
310 DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
311 } while(tries-- && ret);
312 if(!ret)
313 {
314 ret = m_at.executeSimple("ATH", &result);
315 DBG("Result of command: Err code=%d\n", ret);
316 DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
317 }
318 #endif
319
320 #if USE_ONE_PORT
321 //Reinit AT parser
322 ret = m_at.init();
323 DBG("Result of command: Err code=%d\n", ret);
324 if(ret)
325 {
326 m_at.close(); // Closing AT parser
327 DBG("AT Parser closed, could not complete disconnection");
328 return NET_TIMEOUT;
329 }
330
331 #if 0
332 m_at.close(); // Closing AT parser
333 DBG("AT Parser closed");
334 #endif
335 #endif
336 return OK;
337 }
338
339 int UbloxUSBGSMModem::sendSM(const char* number, const char* message)
340 {
341 int ret = init();
342 if(ret)
343 {
344 return ret;
345 }
346
347 if(!m_smsInit)
348 {
349 ret = m_sms.init();
350 if(ret)
351 {
352 return ret;
353 }
354 m_smsInit = true;
355 }
356
357 ret = m_sms.send(number, message);
358 if(ret)
359 {
360 return ret;
361 }
362
363 return OK;
364 }
365
366 int UbloxUSBGSMModem::getSM(char* number, char* message, size_t maxLength)
367 {
368 int ret = init();
369 if(ret)
370 {
371 return ret;
372 }
373
374 if(!m_smsInit)
375 {
376 ret = m_sms.init();
377 if(ret)
378 {
379 return ret;
380 }
381 m_smsInit = true;
382 }
383
384 ret = m_sms.get(number, message, maxLength);
385 if(ret)
386 {
387 return ret;
388 }
389
390 return OK;
391 }
392
393 int UbloxUSBGSMModem::getSMCount(size_t* pCount)
394 {
395 int ret = init();
396 if(ret)
397 {
398 return ret;
399 }
400
401 if(!m_smsInit)
402 {
403 ret = m_sms.init();
404 if(ret)
405 {
406 return ret;
407 }
408 m_smsInit = true;
409 }
410
411 ret = m_sms.getCount(pCount);
412 if(ret)
413 {
414 return ret;
415 }
416
417 return OK;
418 }
419
420 int UbloxUSBGSMModem::sendUSSD(const char* command, char* result, size_t maxLength)
421 {
422 int ret = init();
423 if(ret)
424 {
425 return ret;
426 }
427
428 if(!m_ussdInit)
429 {
430 ret = m_ussd.init();
431 if(ret)
432 {
433 return ret;
434 }
435 m_ussdInit = true;
436 }
437
438 ret = m_ussd.send(command, result, maxLength);
439 if(ret)
440 {
441 return ret;
442 }
443
444 return OK;
445 }
446
447 int UbloxUSBGSMModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
448 {
449 int ret = init();
450 if(ret)
451 {
452 return ret;
453 }
454
455 if(!m_linkMonitorInit)
456 {
457 ret = m_linkMonitor.init();
458 if(ret)
459 {
460 return ret;
461 }
462 m_linkMonitorInit = true;
463 }
464
465 ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
466 if(ret)
467 {
468 return ret;
469 }
470
471 return OK;
472 }
473
474
475 ATCommandsInterface* UbloxUSBGSMModem::getATCommandsInterface()
476 {
477 return &m_at;
478 }
479
480 int UbloxUSBGSMModem::power(bool enable)
481 {
482 if( m_powerGatingPin == NC )
483 {
484 return NET_INVALID; //A pin name has not been provided in the constructor
485 }
486
487 if(!enable) //Will force components to re-init
488 {
489 cleanup();
490 }
491
492 DigitalOut powerGatingOut(m_powerGatingPin);
493 powerGatingOut = m_powerGatingOnWhenPinHigh?enable:!enable;
494
495 return OK;
496 }
497
498 bool UbloxUSBGSMModem::power()
499 {
500 if( m_powerGatingPin == NC )
501 {
502 return true; //Assume power is always on
503 }
504
505 DigitalOut powerGatingOut(m_powerGatingPin);
506 return m_powerGatingOnWhenPinHigh?powerGatingOut:!powerGatingOut;
507 }
508
509 int UbloxUSBGSMModem::init()
510 {
511 if( !m_dongleConnected )
512 {
513 if(!power())
514 {
515 //Obviously cannot initialize the dongle if it is disconnected...
516 ERR("Power is off");
517 return NET_INVALID;
518 }
519 m_dongleConnected = true;
520 while( !m_dongle.connected() )
521 {
522 m_dongle.tryConnect();
523 Thread::wait(10);
524 }
525 }
526
527 if(m_atOpen)
528 {
529 return OK;
530 }
531
532 DBG("Starting AT thread if needed");
533 int ret = m_at.open();
534 if(ret)
535 {
536 return ret;
537 }
538
539 DBG("Sending initialisation commands");
540 ret = m_at.init();
541 if(ret)
542 {
543 return ret;
544 }
545
546 if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200)
547 {
548 INFO("Using a u-blox LISA-U");
549 }
550 else
551 {
552 WARN("Using an Unknown Dongle");
553 }
554
555 ATCommandsInterface::ATResult result;
556
557 //Wait for network registration
558 CREGProcessor cregProcessor;
559 do
560 {
561 DBG("Waiting for network registration");
562 ret = m_at.execute("AT+CREG?", &cregProcessor, &result);
563 DBG("Result of command: Err code=%d\n", ret);
564 DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
565 if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING)
566 {
567 Thread::wait(3000);
568 }
569 } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
570 if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED)
571 {
572 ERR("Registration denied");
573 return NET_AUTH;
574 }
575
576 m_atOpen = true;
577
578 return OK;
579 }
580
581 int UbloxUSBGSMModem::cleanup()
582 {
583 if(m_ppp.isConnected())
584 {
585 WARN("Data connection is still open"); //Try to encourage good behaviour from the user
586 m_ppp.disconnect();
587 }
588
589 m_smsInit = false;
590 m_ussdInit = false;
591 m_linkMonitorInit = false;
592 //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once
593
594 if(m_atOpen)
595 {
596 m_at.close();
597 m_atOpen = false;
598 }
599
600 m_dongle.disconnect();
601 m_dongleConnected = false;
602
603 return OK;
604 }
605
Imprint / Impressum