Implementation of 3G USB Modem Huawei E372

Dependents:   PYRN

PyrnUSBModem.cpp

Committer:
clemounet
Date:
2015-02-20
Revision:
1:fbf17fb09581
Parent:
0:67daedd6f74f
Child:
2:61ac95f0af72

File content as of revision 1:fbf17fb09581:


#include "dbg.h"
#include "PyrnUSBModem.h"
#include "HuaweiUSBModemInitializer.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) {
        USB_DBG("GOT %s",line);
        if(!strncmp(line, "+CPIN: READY",12)) {
            status = STATUS_READY; 
            USB_DBG("STATUS_READY");
        } else if(!strncmp(line, "+CPIN: SIM PIN",14)) {
            status = STATUS_SIMPIN;
            USB_DBG("STATUS_SIMPIN");
        }else if(!strncmp(line, "+CPIN: SIM PUK",14)) {
            status = STATUS_SIMPUK;
            USB_DBG("STATUS_SIMPUK");
        } else
            USB_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;
};

PyrnUSBModem::PyrnUSBModem(USBHost *host): 
        dongle(),   
        atStream(dongle.getSerial(3)),
        pppStream(dongle.getSerial(3)),
        at(&atStream),
        ppp(&pppStream),
        atOpen(false),
        pppOpen(false) {
    // If we support more dongle add the initializer here.
    // furthermore add dongle specific initialisation int init().
    HuaweiE372USBModemInitializer *huaweie372Initializer = new HuaweiE372USBModemInitializer(host);
    dongle.addInitializer(huaweie372Initializer);
}

bool PyrnUSBModem::init() {

    if(!dongle.connected()){
        while(!dongle.connected()) {
          USB_INFO("Dongle try connect");
          dongle.tryConnect();
          Thread::wait(10);
        }
        if(!dongle.connected())
            return false;
    } else 
        USB_INFO("Dongle is already connected ... continue");

    if(atOpen) {
        USB_INFO("Stream is already opened");
        return true;
    }

    USB_INFO("Starting AT thread if needed");
    int ret = at.open();
    if(ret) {
        USB_ERR("Opening AT failed");
        return false;
    }

    USB_INFO("Sending initialisation commands");
    ret = at.init();
    if(ret) {
        USB_ERR("Initialisation AT failed");
        return false;
    }

    if(dongle.getDongleType() == WAN_DONGLE_TYPE_HUAWEI_E372) {
        USB_INFO("Using a Vodafone E372 Dongle");
        // Specific dongle initisation
        ret = at.executeSimple("AT", NULL,5000);
        if(ret != OK){
            USB_ERR("AT Simple command ERROR");
            return false;
        } else {
            USB_INFO("AT Simple command gone well!!");
        }
    } else  {
        USB_WARN("Using an Unknown Dongle");
    }

    ATCommandsInterface::ATResult result;

    // SIM PIN Stuff here
    bool pin = false;
    int retries = 3;
    do {
        CPINProcessor cpinProcessor;
        USB_INFO("Check CPIN STATE");
        ret = at.execute("AT+CPIN?", &cpinProcessor, &result,5000);
        USB_INFO("Result of command: Err code=%d\n", ret);
        USB_INFO("ATResult: AT return=%d (code %d)\n", result.result, result.code);
        if(ret == OK) {
            if(cpinProcessor.getStatus() == CPINProcessor::STATUS_READY) {
                USB_INFO("ME PIN READY\n");
                pin = true;
                break;
            } else if (cpinProcessor.getStatus() == CPINProcessor::STATUS_SIMPIN)  {
                USB_INFO("ME SIM PIN NEEDED\n");
                ret = at.executeSimple("AT+CPIN=\"0000\"", NULL,5000);
                if(ret != OK){
                    USB_ERR("CPIN ERROR ... do not retry");
                    break;
                } else {
                    USB_INFO("CPIN OK");
                }
            } else if (cpinProcessor.getStatus() == CPINProcessor::STATUS_SIMPUK) {
                USB_INFO("CPIN IS PUKED");
                break;
            } else {
                USB_ERR("UNKNOWN STATUS");
                break;
            }
        } else {
            USB_INFO("SIM PIN ERROR: SIM Probably not inserted\n");
            break;
        }
        retries--;
    } while(retries);
    
    if(!pin) {
        USB_ERR("Couldn't pin unlock ...");
        return false;
    }

    //Wait for network registration
    CREGProcessor cregProcessor;
    do {
        USB_INFO("Waiting for network registration");
        ret = at.execute("AT+CREG?", &cregProcessor, &result);
        USB_INFO("Result of command: Err code=%d\n", ret);
        USB_INFO("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) {
        USB_ERR("Registration denied");
        return false;
    }
    
    atOpen = true;

    return true;
}

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

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