]> git.donarmstrong.com Git - qmk_firmware.git/blob - tool/mbed/mbed-sdk/libraries/net/cellular/UbloxUSBModem/UbloxModem.cpp
Squashed 'tmk_core/' changes from 7967731..b9e0ea0
[qmk_firmware.git] / tool / mbed / mbed-sdk / libraries / net / cellular / UbloxUSBModem / UbloxModem.cpp
1 /* UbloxModem.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__ "UbloxModem.cpp"
23 #endif
24
25 #include "core/fwk.h"
26 #include "sms/GSMSMSInterface.h"
27 #include "sms/CDMASMSInterface.h"
28
29 #include "UbloxModem.h"
30
31 UbloxModem::UbloxModem(IOStream* atStream, IOStream* pppStream) :
32    m_at(atStream),                          // Construct ATCommandsInterface with the AT serial channel
33    m_CdmaSms(&m_at),                         // Construct SMSInterface with the ATCommandsInterface
34    m_GsmSms(&m_at),                          // Construct SMSInterface with the ATCommandsInterface
35    m_ussd(&m_at),                           // Construct USSDInterface with the ATCommandsInterface
36    m_linkMonitor(&m_at),                    // Construct LinkMonitor with the ATCommandsInterface
37    m_ppp(pppStream ? pppStream : atStream),    // Construct PPPIPInterface with the PPP serial channel
38    m_ipInit(false),                         // PPIPInterface connection is initially down
39    m_smsInit(false),                        // SMSInterface starts un-initialised
40    m_ussdInit(false),                       // USSDInterface starts un-initialised 
41    m_linkMonitorInit(false),                // LinkMonitor subsystem starts un-initialised
42    m_atOpen(false),                          // ATCommandsInterface starts in a closed state
43    m_onePort(pppStream == NULL),
44    m_type(UNKNOWN)
45 {
46 }
47
48
49 genericAtProcessor::genericAtProcessor()
50
51     i = 0; 
52     str[0] = '\0'; 
53 }
54
55 const char* genericAtProcessor::getResponse(void) 
56
57     return str; 
58 }
59
60 int genericAtProcessor::onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
61 {
62     int l = strlen(line);
63     if (i + l + 2 > sizeof(str))
64         return NET_OVERFLOW;
65     if (i) str[i++] = ',';
66     strcat(&str[i], line);
67     i += l;
68     return OK;
69 }
70
71 int genericAtProcessor::onNewEntryPrompt(ATCommandsInterface* pInst)
72 {
73     return OK;
74 }
75
76 class CREGProcessor : public IATCommandsProcessor
77 {
78 public:
79   CREGProcessor(bool gsm) : status(STATUS_REGISTERING)
80   {
81     m_gsm = gsm;
82   }
83   enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
84   REGISTERING_STATUS getStatus()
85   {
86     return status;
87   }
88   const char* getAtCommand()
89   {
90       return m_gsm ? "AT+CREG?" : "AT+CSS?";
91   }
92 private:
93   virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
94   {
95     int r;
96     if (m_gsm)
97     {
98         if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
99         {
100           status = (r == 1 || r == 5) ? STATUS_OK : 
101                    (r == 0 || r == 2) ? STATUS_REGISTERING :
102           //       (r == 3)           ? STATUS_FAILED :
103                                         STATUS_FAILED;
104         }
105     }
106     else
107     {
108         char bc[3] = "";
109         if(sscanf(line, "%*s %*c,%2s,%*d",bc)==1)
110         {
111             status = (strcmp("Z", bc) == 0) ? STATUS_REGISTERING : STATUS_OK;
112         }
113     }
114     return OK;
115   }
116   virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
117   {
118     return OK;
119   }
120   volatile REGISTERING_STATUS status;
121   bool m_gsm;
122 };
123
124 int UbloxModem::connect(const char* apn, const char* user, const char* password)
125 {
126   if( !m_ipInit )
127   {
128     m_ipInit = true;
129     m_ppp.init();
130   }
131   m_ppp.setup(user, password, (m_type != LISA_C200) ? DEFAULT_MSISDN_GSM : DEFAULT_MSISDN_CDMA);
132
133   int ret = init();
134   if(ret)
135   {
136     return ret;
137   }
138
139   if (m_onePort)
140   {
141      m_smsInit = false; //SMS status reset
142      m_ussdInit = false; //USSD status reset
143      m_linkMonitorInit = false; //Link monitor status reset
144   }
145
146   ATCommandsInterface::ATResult result;
147
148   if(apn != NULL)
149   {
150     char cmd[48];
151     int tries = 30;
152     sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
153     do //Try 30 times because for some reasons it can fail *a lot* with the K3772-Z dongle
154     {
155       ret = m_at.executeSimple(cmd, &result);
156       DBG("Result of command: Err code=%d", ret);
157       if(ret)
158       {
159         Thread::wait(500);
160       }
161     } while(ret && --tries);
162     DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
163     DBG("APN set to %s", apn);
164   }
165
166   //Connect
167   DBG("Connecting");
168   if (m_onePort)
169   {
170     m_at.close(); // Closing AT parser
171     m_atOpen = false; //Will need to be reinitialized afterwards
172   }
173   
174   DBG("Connecting PPP");
175
176   ret = m_ppp.connect();
177   DBG("Result of connect: Err code=%d", ret);
178   return ret;
179 }
180
181
182 int UbloxModem::disconnect()
183 {
184   DBG("Disconnecting from PPP");
185   int ret = m_ppp.disconnect();
186   if(ret)
187   {
188     ERR("Disconnect returned %d, still trying to disconnect", ret);
189   }
190
191   //Ugly but leave dongle time to recover
192   Thread::wait(500);
193
194   if (m_onePort)
195   {
196     //ATCommandsInterface::ATResult result;
197     DBG("Starting AT thread");
198     ret = m_at.open();
199     if(ret)
200     {
201       return ret;
202     }
203   }
204
205   DBG("Trying to hangup");
206
207   if (m_onePort)
208   {
209     //Reinit AT parser
210     ret = m_at.init(false);
211     DBG("Result of command: Err code=%d\n", ret);
212     if(ret)
213     {
214       m_at.close(); // Closing AT parser
215       DBG("AT Parser closed, could not complete disconnection");
216       return NET_TIMEOUT;
217     }
218   
219   }
220   return OK;
221 }
222
223 int UbloxModem::sendSM(const char* number, const char* message)
224 {
225   int ret = init();
226   if(ret)
227   {
228     return ret;
229   }
230
231   ISMSInterface* sms;
232   if (m_type == LISA_C200)  sms = &m_CdmaSms;
233   else                      sms = &m_GsmSms;
234   if(!m_smsInit)
235   {
236     ret = sms->init();
237     if(ret)
238     {
239       return ret;
240     }
241     m_smsInit = true;
242   }
243
244   ret = sms->send(number, message);
245   if(ret)
246   {
247     return ret;
248   }
249
250   return OK;
251 }
252
253 int UbloxModem::getSM(char* number, char* message, size_t maxLength)
254 {
255   int ret = init();
256   if(ret)
257   {
258     return ret;
259   }
260
261   ISMSInterface* sms;
262   if (m_type == LISA_C200)  sms = &m_CdmaSms;
263   else                      sms = &m_GsmSms;
264   if(!m_smsInit)
265   {
266     ret = sms->init();
267     if(ret)
268     {
269       return ret;
270     }
271     m_smsInit = true;
272   }
273
274   ret = sms->get(number, message, maxLength);
275   if(ret)
276   {
277     return ret;
278   }
279
280   return OK;
281 }
282
283 int UbloxModem::getSMCount(size_t* pCount)
284 {
285   int ret = init();
286   if(ret)
287   {
288     return ret;
289   }
290
291   ISMSInterface* sms;
292   if (m_type == LISA_C200)  sms = &m_CdmaSms;
293   else                      sms = &m_GsmSms;
294   if(!m_smsInit)
295   {
296     ret = sms->init();
297     if(ret)
298     {
299       return ret;
300     }
301     m_smsInit = true;
302   }
303
304   ret = sms->getCount(pCount);
305   if(ret)
306   {
307     return ret;
308   }
309
310   return OK;
311 }
312
313 ATCommandsInterface* UbloxModem::getATCommandsInterface()
314 {
315   return &m_at;
316 }
317
318 int UbloxModem::init()
319 {
320   if(m_atOpen)
321   {
322     return OK;
323   }
324   
325   DBG("Starting AT thread if needed");
326   int ret = m_at.open();
327   if(ret)
328   {
329     return ret;
330   }
331   
332   DBG("Sending initialisation commands");
333   ret = m_at.init(false);
334   if(ret)
335   {
336     return ret;
337   }
338   
339   
340   ATCommandsInterface::ATResult result;
341   genericAtProcessor atiProcessor;
342   ret = m_at.execute("ATI", &atiProcessor, &result);
343   if (OK != ret)
344     return ret;
345   const char* info = atiProcessor.getResponse();
346   INFO("Modem Identification [%s]", info);
347   if (strstr(info, "LISA-C200")) {
348       m_type = LISA_C200;
349       m_onePort = true; // force use of only one port
350   }
351   else if (strstr(info, "LISA-U200")) {
352       m_type = LISA_U200;
353   }
354   else if (strstr(info, "SARA-G350")) {
355       m_type = SARA_G350;
356   }
357   
358   // enable the network indicator 
359   if (m_type == SARA_G350) {
360       m_at.executeSimple("AT+UGPIOC=16,2", &result);
361   }
362   else if (m_type == LISA_U200) {
363       m_at.executeSimple("AT+UGPIOC=20,2", &result); 
364   }
365   else if (m_type == LISA_C200) {
366       // LISA-C200 02S/22S : GPIO1 do not support network status indication
367       // m_at.executeSimple("AT+UGPIOC=20,2", &result); 
368   }
369   INFO("Modem Identification [%s]", info);
370   
371   CREGProcessor cregProcessor(m_type != LISA_C200);
372   //Wait for network registration
373   do
374   {
375     DBG("Waiting for network registration");
376     ret = m_at.execute(cregProcessor.getAtCommand(), &cregProcessor, &result);
377     DBG("Result of command: Err code=%d\n", ret);
378     DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
379     if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING)
380     {
381       Thread::wait(3000);
382     }
383   } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
384   if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED)
385   {
386     ERR("Registration denied");
387     return NET_AUTH;
388   }
389  
390   m_atOpen = true;
391
392   return OK;
393 }
394
395 int UbloxModem::cleanup()
396 {
397   if(m_ppp.isConnected())
398   {
399     WARN("Data connection is still open"); //Try to encourage good behaviour from the user
400     m_ppp.disconnect(); 
401   }
402   
403   m_smsInit = false;
404   m_ussdInit = false;
405   m_linkMonitorInit = false;
406   //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once
407   
408   if(m_atOpen)
409   {
410     m_at.close();
411     m_atOpen = false;
412   }
413   
414   return OK;
415 }
416
417 int UbloxModem::sendUSSD(const char* command, char* result, size_t maxLength)
418 {
419   int ret = init();
420   if(ret)
421   {
422     return ret;
423   }
424
425   if(!m_ussdInit)
426   {
427     ret = m_ussd.init();
428     if(ret)
429     {
430       return ret;
431     }
432     m_ussdInit = true;
433   }
434
435   ret = m_ussd.send(command, result, maxLength);
436   if(ret)
437   {
438     return ret;
439   }
440
441   return OK;
442 }
443
444 int UbloxModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
445 {
446   int ret = init();
447   if(ret)
448   {
449     return ret;
450   }
451   
452   if(!m_linkMonitorInit)
453   {
454     ret = m_linkMonitor.init(m_type != LISA_C200);
455     if(ret)
456     {
457       return ret;
458     }
459     m_linkMonitorInit = true;
460   }
461
462   ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
463   if(ret)
464   {
465     return ret;
466   }
467
468   return OK;
469 }
470
471 int UbloxModem::getPhoneNumber(char* phoneNumber)
472 {
473   int ret = init();
474   if(ret)
475   {
476     return ret;
477   }
478   
479   if(!m_linkMonitorInit)
480   {
481     ret = m_linkMonitor.init(m_type != LISA_C200);
482     if(ret)
483     {
484       return ret;
485     }
486     m_linkMonitorInit = true;
487   }
488
489   ret = m_linkMonitor.getPhoneNumber(phoneNumber);
490   if(ret)
491   {
492     return ret;
493   }
494
495   return OK;
496 }
497
498 #include "USBHost.h"
499 #include "UbloxGSMModemInitializer.h"
500 #include "UbloxCDMAModemInitializer.h"
501
502 UbloxUSBModem::UbloxUSBModem() :
503    UbloxModem(&m_atStream, &m_pppStream),
504    m_dongle(),                              // Construct WANDongle: USB interface with two serial channels to the modem (USBSerialStream objects)
505    m_atStream(m_dongle.getSerial(1)),       // AT commands are sent down one serial channel.
506    m_pppStream(m_dongle.getSerial(0)),      // PPP connections are managed via another serial channel.
507    m_dongleConnected(false)                 // Dongle is initially not ready for anything
508 {
509     USBHost* host = USBHost::getHostInst();
510     m_dongle.addInitializer(new UbloxGSMModemInitializer(host));
511     m_dongle.addInitializer(new UbloxCDMAModemInitializer(host));
512 }
513
514 int UbloxUSBModem::init()
515 {
516   if( !m_dongleConnected )
517   {
518     m_dongleConnected = true;
519     while( !m_dongle.connected() )
520     {
521       m_dongle.tryConnect();
522       Thread::wait(10);
523     }
524     if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200)
525     {
526       INFO("Using a u-blox LISA-U200 3G/WCDMA Modem");
527       m_type = LISA_U200;
528     }
529     else if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAC200)
530     {
531       INFO("Using a u-blox LISA-C200 CDMA Modem");
532       m_type = LISA_C200;
533       m_onePort = true;
534     }
535     else
536     {
537       WARN("Using an Unknown Dongle");
538     }
539   }
540   return UbloxModem::init();
541 }
542
543 int UbloxUSBModem::cleanup()
544 {
545   UbloxModem::cleanup();
546   m_dongle.disconnect();
547   m_dongleConnected = false;
548   return OK;
549 }
550
551 UbloxSerModem::UbloxSerModem() :
552    UbloxModem(&m_atStream, NULL),
553    m_Serial(P0_15/*MDMTXD*/,P0_16/*MDMRXD*/),
554    m_atStream(m_Serial)
555 {
556   m_Serial.baud(115200/*MDMBAUD*/);
557   m_Serial.set_flow_control(SerialBase::RTSCTS, P0_22/*MDMRTS*/, P0_17/*MDMCTS*/);
558 }
559