]> git.donarmstrong.com Git - qmk_firmware.git/blob - tool/mbed/mbed-sdk/libraries/net/cellular/UbloxUSBModem/UbloxUSBGSMModem.cpp
Squashed 'tmk_core/' changes from 7967731..b9e0ea0
[qmk_firmware.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