Implementation of 3G USB Modem Huawei E372

Dependents:   PYRN

PyrnUSBModem.cpp

Committer:
clemounet
Date:
2015-04-14
Revision:
2:61ac95f0af72
Parent:
1:fbf17fb09581

File content as of revision 2:61ac95f0af72:


#include "PyrnUSBModem.h"

#define __DEBUG__ 5
#ifndef __MODULE__
#define __MODULE__ "PyrnUSBModem.cpp"
#endif
#include "MyDebug.h"

// Command Processors

class CPINProcessor : public IATCommandsProcessor {
public:
    enum PIN_STATUS { STATUS_NONE ,STATUS_SIMPIN, STATUS_SIMPUK, STATUS_READY };
    CPINProcessor() : status(STATUS_NONE) { }
    PIN_STATUS getStatus() { return status; }
private:
    virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line) {
        DBG("GOT %s",line);
        if(!strncmp(line, "+CPIN: READY",12)) {
            status = STATUS_READY; 
            DBG("STATUS_READY");
        } else if(!strncmp(line, "+CPIN: SIM PIN",14)) {
            status = STATUS_SIMPIN;
            DBG("STATUS_SIMPIN");
        }else if(!strncmp(line, "+CPIN: SIM PUK",14)) {
            status = STATUS_SIMPUK;
            DBG("STATUS_SIMPUK");
        } else
            DBG("STATUS_NONE");
        return OK;
    }
    virtual int onNewEntryPrompt(ATCommandsInterface* pInst) { return OK; }
    volatile PIN_STATUS status;
};


class CREGProcessor : public IATCommandsProcessor {
public:
    enum PIN_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
    CREGProcessor() : status(STATUS_REGISTERING) { }
    PIN_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 PIN_STATUS status;
};

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;
};

// ==================== MODEM =====================

PyrnUSBModem::PyrnUSBModem():
        initialiser(USBHost::getHostInst()),
        dongle(),   
        atStream(dongle.getSerial(3)),
        pppStream(dongle.getSerial(0)),
        at(&atStream),
        ppp(&pppStream), 
        atOpen(false),
        simReady(false),
        pppOpen(false),
        ipInit(false) {
    DBG("Add E372 dongle initializer");
    dongle.addInitializer(&initialiser);
}

bool PyrnUSBModem::init() {
    int ret = 0;
    
    if(!dongle.connected()){
        bool detectConnectedModem = false;
        for (int x=0; x<5;x++){
            DBG("Trying to connect the dongle");
            dongle.tryConnect();
            if (dongle.connected()) {
                DBG("Great the dongle is connected - I've tried %d times to connect", x);
                detectConnectedModem = true;
                break;                              // Break out of the for loop once the dongle is connected - otherwise try for a while more
            }
            Thread::wait(7000);
        }
        if (!detectConnectedModem) {
            // OK we got this far - so give up trying and let someone know you can't see the modem
            ERR("There is no dongle pluged into the board, or the module does not respond. Is the module/modem switched on?");
            return false;
        }
    } else {
        INFO("Dongle is already connected ... continue");
    }

    if(atOpen) {
        INFO("Stream is already opened go to SIM Check");
    } else {
        
        INFO("Starting AT thread if needed");
        ret = at.open();
        if(ret) {
            ERR("Opening AT failed");
            return false;
        }
    
        INFO("Sending initialisation commands");
        // Echo 1
        // Format CRLF
        // Unsollicited Codes disabled
        ret = at.init("ATZ E1 V1 ^CURC=0");
        if(ret) {
            ERR("Initialisation AT failed");
            return false;
        }
        
        if(dongle.getDongleType() == WAN_DONGLE_TYPE_HUAWEI_E372) {
            INFO("Using a Vodafone E372 Dongle");
            ERR("Send CMEE cmd ...");
            ret = at.executeSimple("AT+CMEE=1",NULL,5000);
            if(ret != OK) {
                ERR("CMEE cmd failed");
                return false;
            }
        } else  {
            WARN("Using an Unknown Dongle.. do specific init");
        }
        
        atOpen = true;
    }

    ATCommandsInterface::ATResult result;

    if(!simReady){
        // SIM PIN Stuff here
        int retries = 3;
        do {
            CPINProcessor cpinProcessor;
            INFO("Check CPIN STATE");
            ret = at.execute("AT+CPIN?", &cpinProcessor, &result,5000);
            //INFO("Result of command: Err code=%d", ret);
            //INFO("ATResult: AT return=%d (code %d)", result.result, result.code);
            if(result.code == 14) {
                 WARN("SIM IS Busy retry");
                 retries++;
                 Thread::wait(500);
            } else if(ret == OK) {
                if(cpinProcessor.getStatus() == CPINProcessor::STATUS_READY) {
                    INFO("ME PIN READY");
                    simReady = true;
                    break;
                } else if (cpinProcessor.getStatus() == CPINProcessor::STATUS_SIMPIN)  {
                    INFO("ME SIM PIN NEEDED");
                    ret = at.executeSimple("AT+CPIN=\"0000\"",NULL,5000);
                    if(ret != OK){
                        ERR("CPIN ERROR ... do not retry");
                        break;
                    } else {
                        INFO("CPIN OK");
                    }
                } else if (cpinProcessor.getStatus() == CPINProcessor::STATUS_SIMPUK) {
                    INFO("CPIN IS PUKED");
                    break;
                } else {
                    ERR("UNKNOWN STATUS");
                    break;
                }
            } else {
                INFO("SIM PIN ERROR: SIM Probably not inserted");
                break;
            }
            retries--;
        } while(retries);
        
        if(!simReady) {
            ERR("Couldn't pin unlock ...");
            return false;
        }
    } else {
        INFO("SIM PIN have been unlocked somewhere!");
    }
    
    //Wait for network registration
    CREGProcessor cregProcessor;
    do {
        INFO("Waiting for network registration");
        ret = at.execute("AT+CREG?", &cregProcessor, &result);
        //INFO("Result of command: Err code=%d", ret);
        //INFO("ATResult: AT return=%d (code %d)", result.result, result.code);
        if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING) {
            Thread::wait(500);
        }
    } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
    if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED) {
        ERR("Registration denied");
        return false;
    }

    return true;
}

bool PyrnUSBModem::attached(void) {
    return dongle.connected();
}

bool PyrnUSBModem::pppConnected(void) {
    return ppp.isConnected();
}

/*
WANDongleSerialPort *PyrnUSBModem::getAtInterface(int i) {
    return dongle.getWANSerial(i);
}*/

int PyrnUSBModem::connect(const char* apn, const char* user, const char* password) {
    int ret;

    if(!init()) {
        ERR("Modem could not register");
        return -1;
    }
       
    ATCommandsInterface::ATResult result;

    if(apn != NULL) {
        char cmd[48];
        int tries = 30;
        DBG("Setting APN to %s", apn);
        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 = 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("Connecting PPP");
    ret = ppp.connect(user, password);
    DBG("Result of connect: Err code=%d", ret);
    
    return ret;
}

int PyrnUSBModem::disconnect() {
    DBG("Disconnecting from PPP");
    int ret = ppp.disconnect();
    if(ret) {
        ERR("Disconnect returned %d, still trying to disconnect", ret);
    }

    Thread::wait(500);

    return OK;
}

char* PyrnUSBModem::getIPAddress() {
    // return NULL;//ppp.getIPAddress();
    return ppp.getIPAddress();
}