1 /* UbloxUSBGSMModem.cpp */
2 /* Copyright (C) 2012 mbed.org, MIT License
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:
10 * The above copyright notice and this permission notice shall be included in all copies or
11 * substantial portions of the Software.
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.
22 #define __MODULE__ "UbloxUSBGSMModem.cpp"
27 #include "UbloxUSBGSMModem.h"
28 #include "UbloxGSMModemInitializer.h"
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
49 USBHost* host = USBHost::getHostInst();
50 m_dongle.addInitializer(new UbloxGSMModemInitializer(host));
51 if( m_powerGatingPin != NC )
53 power(false); //Dongle will have to be powered on manually
57 class CREGProcessor : public IATCommandsProcessor
60 CREGProcessor() : status(STATUS_REGISTERING)
64 enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
65 REGISTERING_STATUS getStatus()
70 virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
73 if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
83 status = STATUS_REGISTERING;
87 status = STATUS_FAILED;
93 virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
97 volatile REGISTERING_STATUS status;
101 class COPSProcessor : public IATCommandsProcessor
104 COPSProcessor() : valid(false)
127 virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
131 int s = sscanf(line, "+COPS: %*d,%*d,\"%d\",%d", &networkId, &bearerId);
137 strcpy(network, "Vodafone UK");
138 strcpy(apn, "pp.vodafone.co.uk");
142 strcpy(network, "SFR FR");
143 strcpy(apn, "websfr");
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;
170 virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
181 int UbloxUSBGSMModem::connect(const char* apn, const char* user, const char* password)
188 m_ppp.setup(user, password, DEFAULT_MSISDN_GSM);
197 m_smsInit = false; //SMS status reset
198 m_ussdInit = false; //USSD status reset
199 m_linkMonitorInit = false; //Link monitor status reset
202 ATCommandsInterface::ATResult result;
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);
212 if(!copsProcessor.isValid())
214 WARN("Connected to an unknown network, try to connect with default parameters");
215 DBG("Connected with %s", copsProcessor.getBearer());
219 DBG("Connected to %s with %s", copsProcessor.getNetwork(), copsProcessor.getBearer());
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
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());
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
239 ret = m_at.executeSimple(cmd, &result);
240 DBG("Result of command: Err code=%d", ret);
245 } while(ret && --tries);
246 DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
247 DBG("APN set to %s", apn);
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);
260 m_at.close(); // Closing AT parser
261 m_atOpen = false; //Will need to be reinitialized afterwards
265 DBG("AT Parser closed");
266 if( (ret!=NET_MOREINFO) || (result.result != ATCommandsInterface::ATResult::AT_CONNECT))
268 ERR("Could not connect");
269 return ret; //Could not connect
272 DBG("Connecting PPP");
274 ret = m_ppp.connect();
275 DBG("Result of connect: Err code=%d", ret);
280 int UbloxUSBGSMModem::disconnect()
282 DBG("Disconnecting from PPP");
283 int ret = m_ppp.disconnect();
286 ERR("Disconnect returned %d, still trying to disconnect", ret);
289 //Ugly but leave dongle time to recover
293 ATCommandsInterface::ATResult result;
294 DBG("Starting AT thread");
302 DBG("Trying to hangup");
304 #if 0 //Does not appear to work
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);
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);
323 DBG("Result of command: Err code=%d\n", ret);
326 m_at.close(); // Closing AT parser
327 DBG("AT Parser closed, could not complete disconnection");
332 m_at.close(); // Closing AT parser
333 DBG("AT Parser closed");
339 int UbloxUSBGSMModem::sendSM(const char* number, const char* message)
357 ret = m_sms.send(number, message);
366 int UbloxUSBGSMModem::getSM(char* number, char* message, size_t maxLength)
384 ret = m_sms.get(number, message, maxLength);
393 int UbloxUSBGSMModem::getSMCount(size_t* pCount)
411 ret = m_sms.getCount(pCount);
420 int UbloxUSBGSMModem::sendUSSD(const char* command, char* result, size_t maxLength)
438 ret = m_ussd.send(command, result, maxLength);
447 int UbloxUSBGSMModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
455 if(!m_linkMonitorInit)
457 ret = m_linkMonitor.init();
462 m_linkMonitorInit = true;
465 ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
475 ATCommandsInterface* UbloxUSBGSMModem::getATCommandsInterface()
480 int UbloxUSBGSMModem::power(bool enable)
482 if( m_powerGatingPin == NC )
484 return NET_INVALID; //A pin name has not been provided in the constructor
487 if(!enable) //Will force components to re-init
492 DigitalOut powerGatingOut(m_powerGatingPin);
493 powerGatingOut = m_powerGatingOnWhenPinHigh?enable:!enable;
498 bool UbloxUSBGSMModem::power()
500 if( m_powerGatingPin == NC )
502 return true; //Assume power is always on
505 DigitalOut powerGatingOut(m_powerGatingPin);
506 return m_powerGatingOnWhenPinHigh?powerGatingOut:!powerGatingOut;
509 int UbloxUSBGSMModem::init()
511 if( !m_dongleConnected )
515 //Obviously cannot initialize the dongle if it is disconnected...
519 m_dongleConnected = true;
520 while( !m_dongle.connected() )
522 m_dongle.tryConnect();
532 DBG("Starting AT thread if needed");
533 int ret = m_at.open();
539 DBG("Sending initialisation commands");
546 if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200)
548 INFO("Using a u-blox LISA-U");
552 WARN("Using an Unknown Dongle");
555 ATCommandsInterface::ATResult result;
557 //Wait for network registration
558 CREGProcessor cregProcessor;
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)
569 } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
570 if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED)
572 ERR("Registration denied");
581 int UbloxUSBGSMModem::cleanup()
583 if(m_ppp.isConnected())
585 WARN("Data connection is still open"); //Try to encourage good behaviour from the user
591 m_linkMonitorInit = false;
592 //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once
600 m_dongle.disconnect();
601 m_dongleConnected = false;