1
0
tmk_keyboard/tool/mbed/mbed-sdk/libraries/net/cellular/UbloxUSBModem/UbloxUSBGSMModem.cpp
Jun Wako 1fe4406f37 Squashed 'tmk_core/' changes from 7967731..b9e0ea0
b9e0ea0 Merge commit '7fa9d8bdea3773d1195b04d98fcf27cf48ddd81d' as 'tool/mbed/mbed-sdk'
7fa9d8b Squashed 'tool/mbed/mbed-sdk/' content from commit 7c21ce5

git-subtree-dir: tmk_core
git-subtree-split: b9e0ea08cb940de20b3610ecdda18e9d8cd7c552
2015-04-24 16:26:14 +09:00

606 lines
14 KiB
C++

/* UbloxUSBGSMModem.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__ "UbloxUSBGSMModem.cpp"
#endif
#include "core/fwk.h"
#include "UbloxUSBGSMModem.h"
#include "UbloxGSMModemInitializer.h"
#include "USBHost.h"
UbloxUSBGSMModem::UbloxUSBGSMModem(PinName powerGatingPin /*= NC*/, bool powerGatingOnWhenPinHigh /* = true*/) :
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_at(&m_atStream), // Construct ATCommandsInterface with the AT serial channel
m_sms(&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(&m_pppStream), // Construct PPPIPInterface with the PPP serial channel
m_dongleConnected(false), // Dongle is initially not ready for anything
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_powerGatingPin(powerGatingPin), // set power gating pin
m_powerGatingOnWhenPinHigh(powerGatingOnWhenPinHigh) // set state semantics for power gating pin
{
USBHost* host = USBHost::getHostInst();
m_dongle.addInitializer(new UbloxGSMModemInitializer(host));
if( m_powerGatingPin != NC )
{
power(false); //Dongle will have to be powered on manually
}
}
class CREGProcessor : public IATCommandsProcessor
{
public:
CREGProcessor() : status(STATUS_REGISTERING)
{
}
enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
REGISTERING_STATUS getStatus()
{
return status;
}
private:
virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
{
int r;
if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
{
switch(r)
{
case 1:
case 5:
status = STATUS_OK;
break;
case 0:
case 2:
status = STATUS_REGISTERING;
break;
case 3:
default:
status = STATUS_FAILED;
break;
}
}
return OK;
}
virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
{
return OK;
}
volatile REGISTERING_STATUS status;
};
#if 0
class COPSProcessor : public IATCommandsProcessor
{
public:
COPSProcessor() : valid(false)
{
network[0] = '\0';
apn[0] = '\0';
bearer[0] = '\0';
}
char* getNetwork()
{
return network;
}
char* getAPN()
{
return apn;
}
char* getBearer()
{
return bearer;
}
bool isValid()
{
return valid;
}
private:
virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
{
int networkId;
int bearerId;
int s = sscanf(line, "+COPS: %*d,%*d,\"%d\",%d", &networkId, &bearerId);
if( s == 2 )
{
switch(networkId)
{
case 23415:
strcpy(network, "Vodafone UK");
strcpy(apn, "pp.vodafone.co.uk");
valid = true;
break;
case 20810:
strcpy(network, "SFR FR");
strcpy(apn, "websfr");
valid = true;
break;
default:
break;
}
}
else
{
return OK;
}
switch(bearerId)
{
case 0: strcpy(bearer, "GSM"); break;
case 1: strcpy(bearer, "GSM Compact"); break;
case 2: strcpy(bearer, "UTRAN"); break;
case 3: strcpy(bearer, "GSM w/EGPRS"); break;
case 4: strcpy(bearer, "UTRAN w/HSDPA"); break;
case 5: strcpy(bearer, "UTRAN w/HSUPA"); break;
case 6: strcpy(bearer, "UTRAN w/HSDPA and HSUPA"); break;
case 7: strcpy(bearer, "E-UTRAN"); break;
default:
break;
}
return OK;
}
virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
{
return OK;
}
char network[24];
char bearer[24];
char apn[24];
volatile bool valid;
};
#endif
int UbloxUSBGSMModem::connect(const char* apn, const char* user, const char* password)
{
if( !m_ipInit )
{
m_ipInit = true;
m_ppp.init();
}
m_ppp.setup(user, password, DEFAULT_MSISDN_GSM);
int ret = init();
if(ret)
{
return ret;
}
#if USE_ONE_PORT
m_smsInit = false; //SMS status reset
m_ussdInit = false; //USSD status reset
m_linkMonitorInit = false; //Link monitor status reset
#endif
ATCommandsInterface::ATResult result;
#if 0
//Get network info & select corresponding APN
COPSProcessor copsProcessor;
DBG("Get network info & select APN from DB");
ret = m_at.execute("AT+COPS=,2;+COPS?", &copsProcessor, &result); //Configure to get operator's info in numeric code & get operator's id
DBG("Result of command: Err code=%d", ret);
DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
if(!copsProcessor.isValid())
{
WARN("Connected to an unknown network, try to connect with default parameters");
DBG("Connected with %s", copsProcessor.getBearer());
}
else
{
DBG("Connected to %s with %s", copsProcessor.getNetwork(), copsProcessor.getBearer());
char cmd[48];
int tries = 3;
sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", copsProcessor.getAPN());
do //Try 3 times because for some reasons it can fail with the K3772-Z dongle
{
ret = m_at.executeSimple(cmd, &result);
DBG("Result of command: Err code=%d", ret);
} while(ret && --tries);
DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
DBG("APN set to %s", copsProcessor.getAPN());
}
#else
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);
}
#endif
//Connect
DBG("Connecting");
#if 0
ret = m_at.executeSimple("ATDT *99#", &result);
DBG("Result of command: Err code=%d", ret);
DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
#endif
#if USE_ONE_PORT
m_at.close(); // Closing AT parser
m_atOpen = false; //Will need to be reinitialized afterwards
#endif
#if 0
DBG("AT Parser closed");
if( (ret!=NET_MOREINFO) || (result.result != ATCommandsInterface::ATResult::AT_CONNECT))
{
ERR("Could not connect");
return ret; //Could not connect
}
#endif
DBG("Connecting PPP");
ret = m_ppp.connect();
DBG("Result of connect: Err code=%d", ret);
return ret;
}
int UbloxUSBGSMModem::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 USE_ONE_PORT
ATCommandsInterface::ATResult result;
DBG("Starting AT thread");
ret = m_at.open();
if(ret)
{
return ret;
}
#endif
DBG("Trying to hangup");
#if 0 //Does not appear to work
int tries = 10;
do
{
ret = m_at.executeSimple("+++", &result, 1000);
DBG("Result of command: Err code=%d\n", ret);
DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
} while(tries-- && ret);
if(!ret)
{
ret = m_at.executeSimple("ATH", &result);
DBG("Result of command: Err code=%d\n", ret);
DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
}
#endif
#if USE_ONE_PORT
//Reinit AT parser
ret = m_at.init();
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;
}
#if 0
m_at.close(); // Closing AT parser
DBG("AT Parser closed");
#endif
#endif
return OK;
}
int UbloxUSBGSMModem::sendSM(const char* number, const char* message)
{
int ret = init();
if(ret)
{
return ret;
}
if(!m_smsInit)
{
ret = m_sms.init();
if(ret)
{
return ret;
}
m_smsInit = true;
}
ret = m_sms.send(number, message);
if(ret)
{
return ret;
}
return OK;
}
int UbloxUSBGSMModem::getSM(char* number, char* message, size_t maxLength)
{
int ret = init();
if(ret)
{
return ret;
}
if(!m_smsInit)
{
ret = m_sms.init();
if(ret)
{
return ret;
}
m_smsInit = true;
}
ret = m_sms.get(number, message, maxLength);
if(ret)
{
return ret;
}
return OK;
}
int UbloxUSBGSMModem::getSMCount(size_t* pCount)
{
int ret = init();
if(ret)
{
return ret;
}
if(!m_smsInit)
{
ret = m_sms.init();
if(ret)
{
return ret;
}
m_smsInit = true;
}
ret = m_sms.getCount(pCount);
if(ret)
{
return ret;
}
return OK;
}
int UbloxUSBGSMModem::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 UbloxUSBGSMModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
{
int ret = init();
if(ret)
{
return ret;
}
if(!m_linkMonitorInit)
{
ret = m_linkMonitor.init();
if(ret)
{
return ret;
}
m_linkMonitorInit = true;
}
ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
if(ret)
{
return ret;
}
return OK;
}
ATCommandsInterface* UbloxUSBGSMModem::getATCommandsInterface()
{
return &m_at;
}
int UbloxUSBGSMModem::power(bool enable)
{
if( m_powerGatingPin == NC )
{
return NET_INVALID; //A pin name has not been provided in the constructor
}
if(!enable) //Will force components to re-init
{
cleanup();
}
DigitalOut powerGatingOut(m_powerGatingPin);
powerGatingOut = m_powerGatingOnWhenPinHigh?enable:!enable;
return OK;
}
bool UbloxUSBGSMModem::power()
{
if( m_powerGatingPin == NC )
{
return true; //Assume power is always on
}
DigitalOut powerGatingOut(m_powerGatingPin);
return m_powerGatingOnWhenPinHigh?powerGatingOut:!powerGatingOut;
}
int UbloxUSBGSMModem::init()
{
if( !m_dongleConnected )
{
if(!power())
{
//Obviously cannot initialize the dongle if it is disconnected...
ERR("Power is off");
return NET_INVALID;
}
m_dongleConnected = true;
while( !m_dongle.connected() )
{
m_dongle.tryConnect();
Thread::wait(10);
}
}
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();
if(ret)
{
return ret;
}
if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200)
{
INFO("Using a u-blox LISA-U");
}
else
{
WARN("Using an Unknown Dongle");
}
ATCommandsInterface::ATResult result;
//Wait for network registration
CREGProcessor cregProcessor;
do
{
DBG("Waiting for network registration");
ret = m_at.execute("AT+CREG?", &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 UbloxUSBGSMModem::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;
}
m_dongle.disconnect();
m_dongleConnected = false;
return OK;
}