mbed-os
Fork of mbed-os by
Diff: features/unsupported/net/cellular/UbloxUSBModem/UbloxModem.cpp
- Revision:
- 0:f269e3021894
diff -r 000000000000 -r f269e3021894 features/unsupported/net/cellular/UbloxUSBModem/UbloxModem.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/unsupported/net/cellular/UbloxUSBModem/UbloxModem.cpp Sun Oct 23 15:10:02 2016 +0000 @@ -0,0 +1,559 @@ +/* UbloxModem.cpp */ +/* Copyright (C) 2012 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define __DEBUG__ 3 +#ifndef __MODULE__ +#define __MODULE__ "UbloxModem.cpp" +#endif + +#include "core/fwk.h" +#include "sms/GSMSMSInterface.h" +#include "sms/CDMASMSInterface.h" + +#include "UbloxModem.h" + +UbloxModem::UbloxModem(IOStream* atStream, IOStream* pppStream) : + m_at(atStream), // Construct ATCommandsInterface with the AT serial channel + m_CdmaSms(&m_at), // Construct SMSInterface with the ATCommandsInterface + m_GsmSms(&m_at), // Construct SMSInterface with the ATCommandsInterface + m_ussd(&m_at), // Construct USSDInterface with the ATCommandsInterface + m_linkMonitor(&m_at), // Construct LinkMonitor with the ATCommandsInterface + m_ppp(pppStream ? pppStream : atStream), // Construct PPPIPInterface with the PPP serial channel + m_ipInit(false), // PPIPInterface connection is initially down + m_smsInit(false), // SMSInterface starts un-initialised + m_ussdInit(false), // USSDInterface starts un-initialised + m_linkMonitorInit(false), // LinkMonitor subsystem starts un-initialised + m_atOpen(false), // ATCommandsInterface starts in a closed state + m_onePort(pppStream == NULL), + m_type(UNKNOWN) +{ +} + + +genericAtProcessor::genericAtProcessor() +{ + i = 0; + str[0] = '\0'; +} + +const char* genericAtProcessor::getResponse(void) +{ + return str; +} + +int genericAtProcessor::onNewATResponseLine(ATCommandsInterface* pInst, const char* line) +{ + int l = strlen(line); + if (i + l + 2 > sizeof(str)) + return NET_OVERFLOW; + if (i) str[i++] = ','; + strcat(&str[i], line); + i += l; + return OK; +} + +int genericAtProcessor::onNewEntryPrompt(ATCommandsInterface* pInst) +{ + return OK; +} + +class CREGProcessor : public IATCommandsProcessor +{ +public: + CREGProcessor(bool gsm) : status(STATUS_REGISTERING) + { + m_gsm = gsm; + } + enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED }; + REGISTERING_STATUS getStatus() + { + return status; + } + const char* getAtCommand() + { + return m_gsm ? "AT+CREG?" : "AT+CSS?"; + } +private: + virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line) + { + int r; + if (m_gsm) + { + if( sscanf(line, "+CREG: %*d,%d", &r) == 1 ) + { + status = (r == 1 || r == 5) ? STATUS_OK : + (r == 0 || r == 2) ? STATUS_REGISTERING : + // (r == 3) ? STATUS_FAILED : + STATUS_FAILED; + } + } + else + { + char bc[3] = ""; + if(sscanf(line, "%*s %*c,%2s,%*d",bc)==1) + { + status = (strcmp("Z", bc) == 0) ? STATUS_REGISTERING : STATUS_OK; + } + } + return OK; + } + virtual int onNewEntryPrompt(ATCommandsInterface* pInst) + { + return OK; + } + volatile REGISTERING_STATUS status; + bool m_gsm; +}; + +int UbloxModem::connect(const char* apn, const char* user, const char* password) +{ + if( !m_ipInit ) + { + m_ipInit = true; + m_ppp.init(); + } + m_ppp.setup(user, password, (m_type != LISA_C200) ? DEFAULT_MSISDN_GSM : DEFAULT_MSISDN_CDMA); + + int ret = init(); + if(ret) + { + return ret; + } + + if (m_onePort) + { + m_smsInit = false; //SMS status reset + m_ussdInit = false; //USSD status reset + m_linkMonitorInit = false; //Link monitor status reset + } + + ATCommandsInterface::ATResult result; + + if(apn != NULL) + { + char cmd[48]; + int tries = 30; + sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", apn); + do //Try 30 times because for some reasons it can fail *a lot* with the K3772-Z dongle + { + ret = m_at.executeSimple(cmd, &result); + DBG("Result of command: Err code=%d", ret); + if(ret) + { + Thread::wait(500); + } + } while(ret && --tries); + DBG("ATResult: AT return=%d (code %d)", result.result, result.code); + DBG("APN set to %s", apn); + } + + //Connect + DBG("Connecting"); + if (m_onePort) + { + m_at.close(); // Closing AT parser + m_atOpen = false; //Will need to be reinitialized afterwards + } + + DBG("Connecting PPP"); + + ret = m_ppp.connect(); + DBG("Result of connect: Err code=%d", ret); + return ret; +} + + +int UbloxModem::disconnect() +{ + DBG("Disconnecting from PPP"); + int ret = m_ppp.disconnect(); + if(ret) + { + ERR("Disconnect returned %d, still trying to disconnect", ret); + } + + //Ugly but leave dongle time to recover + Thread::wait(500); + + if (m_onePort) + { + //ATCommandsInterface::ATResult result; + DBG("Starting AT thread"); + ret = m_at.open(); + if(ret) + { + return ret; + } + } + + DBG("Trying to hangup"); + + if (m_onePort) + { + //Reinit AT parser + ret = m_at.init(false); + DBG("Result of command: Err code=%d\n", ret); + if(ret) + { + m_at.close(); // Closing AT parser + DBG("AT Parser closed, could not complete disconnection"); + return NET_TIMEOUT; + } + + } + return OK; +} + +int UbloxModem::sendSM(const char* number, const char* message) +{ + int ret = init(); + if(ret) + { + return ret; + } + + ISMSInterface* sms; + if (m_type == LISA_C200) sms = &m_CdmaSms; + else sms = &m_GsmSms; + if(!m_smsInit) + { + ret = sms->init(); + if(ret) + { + return ret; + } + m_smsInit = true; + } + + ret = sms->send(number, message); + if(ret) + { + return ret; + } + + return OK; +} + +int UbloxModem::getSM(char* number, char* message, size_t maxLength) +{ + int ret = init(); + if(ret) + { + return ret; + } + + ISMSInterface* sms; + if (m_type == LISA_C200) sms = &m_CdmaSms; + else sms = &m_GsmSms; + if(!m_smsInit) + { + ret = sms->init(); + if(ret) + { + return ret; + } + m_smsInit = true; + } + + ret = sms->get(number, message, maxLength); + if(ret) + { + return ret; + } + + return OK; +} + +int UbloxModem::getSMCount(size_t* pCount) +{ + int ret = init(); + if(ret) + { + return ret; + } + + ISMSInterface* sms; + if (m_type == LISA_C200) sms = &m_CdmaSms; + else sms = &m_GsmSms; + if(!m_smsInit) + { + ret = sms->init(); + if(ret) + { + return ret; + } + m_smsInit = true; + } + + ret = sms->getCount(pCount); + if(ret) + { + return ret; + } + + return OK; +} + +ATCommandsInterface* UbloxModem::getATCommandsInterface() +{ + return &m_at; +} + +int UbloxModem::init() +{ + if(m_atOpen) + { + return OK; + } + + DBG("Starting AT thread if needed"); + int ret = m_at.open(); + if(ret) + { + return ret; + } + + DBG("Sending initialisation commands"); + ret = m_at.init(false); + if(ret) + { + return ret; + } + + + ATCommandsInterface::ATResult result; + genericAtProcessor atiProcessor; + ret = m_at.execute("ATI", &atiProcessor, &result); + if (OK != ret) + return ret; + const char* info = atiProcessor.getResponse(); + INFO("Modem Identification [%s]", info); + if (strstr(info, "LISA-C200")) { + m_type = LISA_C200; + m_onePort = true; // force use of only one port + } + else if (strstr(info, "LISA-U200")) { + m_type = LISA_U200; + } + else if (strstr(info, "SARA-G350")) { + m_type = SARA_G350; + } + + // enable the network indicator + if (m_type == SARA_G350) { + m_at.executeSimple("AT+UGPIOC=16,2", &result); + } + else if (m_type == LISA_U200) { + m_at.executeSimple("AT+UGPIOC=20,2", &result); + } + else if (m_type == LISA_C200) { + // LISA-C200 02S/22S : GPIO1 do not support network status indication + // m_at.executeSimple("AT+UGPIOC=20,2", &result); + } + INFO("Modem Identification [%s]", info); + + CREGProcessor cregProcessor(m_type != LISA_C200); + //Wait for network registration + do + { + DBG("Waiting for network registration"); + ret = m_at.execute(cregProcessor.getAtCommand(), &cregProcessor, &result); + DBG("Result of command: Err code=%d\n", ret); + DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code); + if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING) + { + Thread::wait(3000); + } + } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING); + if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED) + { + ERR("Registration denied"); + return NET_AUTH; + } + + m_atOpen = true; + + return OK; +} + +int UbloxModem::cleanup() +{ + if(m_ppp.isConnected()) + { + WARN("Data connection is still open"); //Try to encourage good behaviour from the user + m_ppp.disconnect(); + } + + m_smsInit = false; + m_ussdInit = false; + m_linkMonitorInit = false; + //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once + + if(m_atOpen) + { + m_at.close(); + m_atOpen = false; + } + + return OK; +} + +int UbloxModem::sendUSSD(const char* command, char* result, size_t maxLength) +{ + int ret = init(); + if(ret) + { + return ret; + } + + if(!m_ussdInit) + { + ret = m_ussd.init(); + if(ret) + { + return ret; + } + m_ussdInit = true; + } + + ret = m_ussd.send(command, result, maxLength); + if(ret) + { + return ret; + } + + return OK; +} + +int UbloxModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer) +{ + int ret = init(); + if(ret) + { + return ret; + } + + if(!m_linkMonitorInit) + { + ret = m_linkMonitor.init(m_type != LISA_C200); + if(ret) + { + return ret; + } + m_linkMonitorInit = true; + } + + ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer); + if(ret) + { + return ret; + } + + return OK; +} + +int UbloxModem::getPhoneNumber(char* phoneNumber) +{ + int ret = init(); + if(ret) + { + return ret; + } + + if(!m_linkMonitorInit) + { + ret = m_linkMonitor.init(m_type != LISA_C200); + if(ret) + { + return ret; + } + m_linkMonitorInit = true; + } + + ret = m_linkMonitor.getPhoneNumber(phoneNumber); + if(ret) + { + return ret; + } + + return OK; +} + +#include "USBHost.h" +#include "UbloxGSMModemInitializer.h" +#include "UbloxCDMAModemInitializer.h" + +UbloxUSBModem::UbloxUSBModem() : + UbloxModem(&m_atStream, &m_pppStream), + m_dongle(), // Construct WANDongle: USB interface with two serial channels to the modem (USBSerialStream objects) + m_atStream(m_dongle.getSerial(1)), // AT commands are sent down one serial channel. + m_pppStream(m_dongle.getSerial(0)), // PPP connections are managed via another serial channel. + m_dongleConnected(false) // Dongle is initially not ready for anything +{ + USBHost* host = USBHost::getHostInst(); + m_dongle.addInitializer(new UbloxGSMModemInitializer(host)); + m_dongle.addInitializer(new UbloxCDMAModemInitializer(host)); +} + +int UbloxUSBModem::init() +{ + if( !m_dongleConnected ) + { + m_dongleConnected = true; + while( !m_dongle.connected() ) + { + m_dongle.tryConnect(); + Thread::wait(10); + } + if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200) + { + INFO("Using a u-blox LISA-U200 3G/WCDMA Modem"); + m_type = LISA_U200; + } + else if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAC200) + { + INFO("Using a u-blox LISA-C200 CDMA Modem"); + m_type = LISA_C200; + m_onePort = true; + } + else + { + WARN("Using an Unknown Dongle"); + } + } + return UbloxModem::init(); +} + +int UbloxUSBModem::cleanup() +{ + UbloxModem::cleanup(); + m_dongle.disconnect(); + m_dongleConnected = false; + return OK; +} + +UbloxSerModem::UbloxSerModem() : + UbloxModem(&m_atStream, NULL), + m_Serial(P0_15/*MDMTXD*/,P0_16/*MDMRXD*/), + m_atStream(m_Serial) +{ + m_Serial.baud(115200/*MDMBAUD*/); + m_Serial.set_flow_control(SerialBase::RTSCTS, P0_22/*MDMRTS*/, P0_17/*MDMCTS*/); +} +