local fork

Dependencies:   Socket USBHostWANDongle_bleedingedge lwip-sys lwip

Dependents:   Encrypted

Fork of VodafoneUSBModem_bleedingedge by Donatien Garnier

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers VodafoneUSBModem.cpp Source File

VodafoneUSBModem.cpp

00001 /* VodafoneUSBModem.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 
00020 
00021 #define __DEBUG__ 0
00022 
00023 #ifndef __MODULE__
00024 #define __MODULE__ "VodafoneUSBModem.cpp"
00025 #endif
00026 
00027 #include "core/fwk.h"
00028 
00029 #include "VodafoneUSBModem.h"
00030 
00031 VodafoneUSBModem::VodafoneUSBModem(PinName powerGatingPin /*= NC*/, bool powerGatingOnWhenPinHigh /* = true*/) :
00032    m_dongle(),                         // Construct WANDongle: USB interface with two serial channels to the modem (USBSerialStream objects)
00033    m_atStream(m_dongle.getSerial(1)),  // AT commands are sent down one serial channel.
00034    m_pppStream(m_dongle.getSerial(0)), // PPP connections are managed via another serial channel.
00035    m_at(&m_atStream),                  // Construct ATCommandsInterface with the AT serial channel
00036    m_sms(&m_at),                       // Construct SMSInterface with the ATCommandsInterface
00037    m_ussd(&m_at),                      // Construct USSDInterface with the ATCommandsInterface
00038    m_linkMonitor(&m_at),               // Construct LinkMonitor with the ATCommandsInterface
00039    m_ppp(&m_pppStream,&m_atStream,&m_at,false), // Construct PPPIPInterface with the PPP serial channel
00040    m_dongleConnected(false),           // Dongle is initially not ready for anything
00041    m_ipInit(false),                    // PPIPInterface connection is initially down
00042    m_smsInit(false),                   // SMSInterface starts un-initialised
00043    m_ussdInit(false),                  // USSDInterface starts un-initialised 
00044    m_linkMonitorInit(false),           // LinkMonitor subsystem starts un-initialised
00045    m_atOpen(false),                    // ATCommandsInterface starts in a closed state
00046    m_powerGatingPin(powerGatingPin),   // set power gating pin
00047    m_powerGatingOnWhenPinHigh(powerGatingOnWhenPinHigh) // set state semantics for power gating pin
00048 {
00049   if( m_powerGatingPin != NC )
00050   {
00051     power(false); //Dongle will have to be powered on manually
00052   }
00053 }
00054 
00055 class CREGProcessor : public IATCommandsProcessor
00056 {
00057 public:
00058   CREGProcessor() : status(STATUS_REGISTERING)
00059   {
00060 
00061   }
00062   enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
00063   REGISTERING_STATUS getStatus()
00064   {
00065     return status;
00066   }
00067 private:
00068   virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
00069   {
00070     int r;
00071     if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
00072     {
00073       switch(r)
00074       {
00075       case 1:
00076       case 5:
00077         status = STATUS_OK;
00078         break;
00079       case 0:
00080       case 2:
00081         status = STATUS_REGISTERING;
00082         break;
00083       case 3:
00084       default:
00085         status = STATUS_FAILED;
00086         break;
00087       }
00088     }
00089     return OK;
00090   }
00091   virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
00092   {
00093     return OK;
00094   }
00095   volatile REGISTERING_STATUS status;
00096 };
00097 
00098 #if 0
00099 class COPSProcessor : public IATCommandsProcessor
00100 {
00101 public:
00102   COPSProcessor() : valid(false)
00103   {
00104     network[0] = '\0';
00105     apn[0] = '\0';
00106     bearer[0] = '\0';
00107   }
00108   char* getNetwork()
00109   {
00110     return network;
00111   }
00112   char* getAPN()
00113   {
00114     return apn;
00115   }
00116   char* getBearer()
00117   {
00118     return bearer;
00119   }
00120   bool isValid()
00121   {
00122     return valid;
00123   }
00124 private:
00125   virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
00126   {
00127     int networkId;
00128     int bearerId;
00129     int s = sscanf(line, "+COPS: %*d,%*d,\"%d\",%d", &networkId, &bearerId);
00130     if( s == 2 )
00131     {
00132       switch(networkId)
00133       {
00134       case 23415:
00135         strcpy(network, "Vodafone UK");
00136         strcpy(apn, "pp.vodafone.co.uk");
00137         valid = true;
00138         break;
00139       case 20810:
00140         strcpy(network, "SFR FR");
00141         strcpy(apn, "websfr");
00142         valid = true;
00143         break;
00144       default:
00145         break;
00146       }
00147     }
00148     else
00149     {
00150       return OK;
00151     }
00152     switch(bearerId)
00153     {
00154     case 0: strcpy(bearer, "GSM"); break;
00155     case 1: strcpy(bearer, "GSM Compact"); break;
00156     case 2: strcpy(bearer, "UTRAN"); break;
00157     case 3: strcpy(bearer, "GSM w/EGPRS"); break;
00158     case 4: strcpy(bearer, "UTRAN w/HSDPA"); break;
00159     case 5: strcpy(bearer, "UTRAN w/HSUPA"); break;
00160     case 6: strcpy(bearer, "UTRAN w/HSDPA and HSUPA"); break;
00161     case 7: strcpy(bearer, "E-UTRAN"); break;
00162 
00163     default:
00164       break;
00165     }
00166     return OK;
00167   }
00168   virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
00169   {
00170     return OK;
00171   }
00172   char network[24];
00173   char bearer[24];
00174   char apn[24];
00175   volatile bool valid;
00176 };
00177 #endif
00178 
00179 int VodafoneUSBModem::connect(const char* apn, const char* user, const char* password)
00180 {
00181   if( !m_ipInit )
00182   {
00183     m_ipInit = true;
00184     m_ppp.init();
00185   }
00186   m_ppp.setup(user, password);
00187 
00188   int ret = init();
00189   if(ret)
00190   {
00191     return ret;
00192   }
00193 
00194   #if USE_ONE_PORT
00195   m_smsInit = false; //SMS status reset
00196   m_ussdInit = false; //USSD status reset
00197   m_linkMonitorInit = false; //Link monitor status reset
00198   #endif
00199 
00200   ATCommandsInterface::ATResult result;
00201 
00202   #if 0
00203   //Get network info & select corresponding APN
00204   COPSProcessor copsProcessor;
00205   DBG("Get network info & select APN from DB");
00206   ret = m_at.execute("AT+COPS=,2;+COPS?", &copsProcessor, &result); //Configure to get operator's info in numeric code & get operator's id
00207   DBG("Result of command: Err code=%d", ret);
00208   DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00209 
00210   if(!copsProcessor.isValid())
00211   {
00212     WARN("Connected to an unknown network, try to connect with default parameters");
00213     DBG("Connected with %s", copsProcessor.getBearer());
00214   }
00215   else
00216   {
00217     DBG("Connected to %s with %s", copsProcessor.getNetwork(), copsProcessor.getBearer());
00218     char cmd[48];
00219     int tries = 3;
00220     sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", copsProcessor.getAPN());
00221     do //Try 3 times because for some reasons it can fail with the K3772-Z dongle
00222     {
00223       ret = m_at.executeSimple(cmd, &result);
00224       DBG("Result of command: Err code=%d", ret);
00225     } while(ret && --tries);
00226     DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00227     DBG("APN set to %s", copsProcessor.getAPN());
00228   }
00229   #else
00230   if(apn != NULL)
00231   {
00232     char cmd[48];
00233     int tries = 30;
00234     sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
00235     do //Try 30 times because for some reasons it can fail *a lot* with the K3772-Z dongle
00236     {
00237       ret = m_at.executeSimple(cmd, &result);
00238       DBG("Result of command: Err code=%d", ret);
00239       if(ret)
00240       {
00241         Thread::wait(500);
00242       }
00243     } while(ret && --tries);
00244     DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00245     DBG("APN set to %s", apn);
00246   }
00247   #endif
00248 
00249 
00250   //Connect
00251   DBG("Connecting");
00252   #if 0
00253   ret = m_at.executeSimple("ATDT *99#", &result);
00254   DBG("Result of command: Err code=%d", ret);
00255   DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
00256   #endif
00257   #if USE_ONE_PORT
00258   m_at.close(); // Closing AT parser
00259   m_atOpen = false; //Will need to be reinitialized afterwards
00260   #endif
00261 
00262   #if 0
00263   DBG("AT Parser closed");
00264   if( (ret!=NET_MOREINFO) || (result.result != ATCommandsInterface::ATResult::AT_CONNECT))
00265   {
00266     ERR("Could not connect");
00267     return ret; //Could not connect
00268   }
00269   #endif
00270   DBG("Connecting PPP");
00271 
00272   ret = m_ppp.connect();
00273   DBG("Result of connect: Err code=%d", ret);
00274   return ret;
00275 }
00276 
00277 
00278 int VodafoneUSBModem::disconnect()
00279 {
00280   DBG("Disconnecting from PPP");
00281   int ret = m_ppp.disconnect();
00282   if(ret)
00283   {
00284     ERR("Disconnect returned %d, still trying to disconnect", ret);
00285   }
00286 
00287   //Ugly but leave dongle time to recover
00288   Thread::wait(500);
00289 
00290   #if USE_ONE_PORT
00291   ATCommandsInterface::ATResult result;
00292   DBG("Starting AT thread");
00293   ret = m_at.open();
00294   if(ret)
00295   {
00296     return ret;
00297   }
00298   #endif
00299 
00300   DBG("Trying to hangup");
00301 
00302   #if 0 //Does not appear to work
00303   int tries = 10;
00304   do
00305   {
00306     ret = m_at.executeSimple("+++", &result, 1000);
00307     DBG("Result of command: Err code=%d\n", ret);
00308     DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
00309   } while(tries-- && ret);
00310   if(!ret)
00311   {
00312     ret = m_at.executeSimple("ATH", &result);
00313     DBG("Result of command: Err code=%d\n", ret);
00314     DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
00315   }
00316   #endif
00317 
00318   #if USE_ONE_PORT
00319   //Reinit AT parser
00320   ret = m_at.init();
00321   DBG("Result of command: Err code=%d\n", ret);
00322   if(ret)
00323   {
00324     m_at.close(); // Closing AT parser
00325     DBG("AT Parser closed, could not complete disconnection");
00326     return NET_TIMEOUT;
00327   }
00328 
00329   #if 0
00330   m_at.close(); // Closing AT parser
00331   DBG("AT Parser closed");
00332   #endif
00333   #endif
00334   return OK;
00335 }
00336 
00337 int VodafoneUSBModem::sendSM(const char* number, const char* message)
00338 {
00339   int ret = init();
00340   if(ret)
00341   {
00342     return ret;
00343   }
00344 
00345   if(!m_smsInit)
00346   {
00347     ret = m_sms.init();
00348     if(ret)
00349     {
00350       return ret;
00351     }
00352     m_smsInit = true;
00353   }
00354 
00355   ret = m_sms.send(number, message);
00356   if(ret)
00357   {
00358     return ret;
00359   }
00360 
00361   return OK;
00362 }
00363 
00364 int VodafoneUSBModem::getSM(char* number, char* message, size_t maxLength)
00365 {
00366   int ret = init();
00367   if(ret)
00368   {
00369     return ret;
00370   }
00371 
00372   if(!m_smsInit)
00373   {
00374     ret = m_sms.init();
00375     if(ret)
00376     {
00377       return ret;
00378     }
00379     m_smsInit = true;
00380   }
00381 
00382   ret = m_sms.get(number, message, maxLength);
00383   if(ret)
00384   {
00385     return ret;
00386   }
00387 
00388   return OK;
00389 }
00390 
00391 int VodafoneUSBModem::getSMCount(size_t* pCount)
00392 {
00393   int ret = init();
00394   if(ret)
00395   {
00396     return ret;
00397   }
00398 
00399   if(!m_smsInit)
00400   {
00401     ret = m_sms.init();
00402     if(ret)
00403     {
00404       return ret;
00405     }
00406     m_smsInit = true;
00407   }
00408 
00409   ret = m_sms.getCount(pCount);
00410   if(ret)
00411   {
00412     return ret;
00413   }
00414 
00415   return OK;
00416 }
00417 
00418 int VodafoneUSBModem::sendUSSD(const char* command, char* result, size_t maxLength)
00419 {
00420   int ret = init();
00421   if(ret)
00422   {
00423     return ret;
00424   }
00425 
00426   if(!m_ussdInit)
00427   {
00428     ret = m_ussd.init();
00429     if(ret)
00430     {
00431       return ret;
00432     }
00433     m_ussdInit = true;
00434   }
00435 
00436   ret = m_ussd.send(command, result, maxLength);
00437   if(ret)
00438   {
00439     return ret;
00440   }
00441 
00442   return OK;
00443 }
00444 
00445 int VodafoneUSBModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
00446 {
00447   DBG("Entering getLinkState.");
00448   int ret = init();
00449   if(ret)
00450   {
00451     return ret;
00452   }
00453   
00454   if(!m_linkMonitorInit)
00455   {
00456     ret = m_linkMonitor.init();
00457     if(ret)
00458     {
00459       return ret;
00460     }
00461     m_linkMonitorInit = true;
00462   }
00463 
00464   ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
00465   if(ret)
00466   {
00467     return ret;
00468   }
00469 
00470   return OK;
00471 }
00472 
00473 
00474 ATCommandsInterface* VodafoneUSBModem::getATCommandsInterface()
00475 {
00476   return &m_at;
00477 }
00478 
00479 int VodafoneUSBModem::power(bool enable)
00480 {
00481   if( m_powerGatingPin == NC )
00482   {
00483     return NET_INVALID; //A pin name has not been provided in the constructor
00484   }
00485 
00486   if(!enable) //Will force components to re-init
00487   {
00488     cleanup();
00489   }
00490   
00491   DigitalOut powerGatingOut(m_powerGatingPin);
00492   powerGatingOut = m_powerGatingOnWhenPinHigh?enable:!enable;
00493 
00494   return OK;
00495 }
00496 
00497 bool VodafoneUSBModem::power()
00498 {
00499   if( m_powerGatingPin == NC )
00500   {
00501     return true; //Assume power is always on 
00502   }
00503   
00504   DigitalOut powerGatingOut(m_powerGatingPin);
00505   return m_powerGatingOnWhenPinHigh?powerGatingOut:!powerGatingOut;
00506 }
00507 
00508 int VodafoneUSBModem::init()
00509 {
00510   DBG("Entering init method for the VodafoneUSBModem");
00511   if( !m_dongleConnected )
00512   {
00513     DBG("Dongle is not connected");
00514     if(!power())
00515     {
00516       //Obviously cannot initialize the dongle if it is disconnected...
00517       ERR("Power is off");
00518       return NET_INVALID;
00519     }
00520     m_dongleConnected = true;
00521     bool detectConnectedModem = false;          // local variable to use to create a while loop that we can break out of - this is used to detect if we can see a modem or not
00522     
00523     while(!detectConnectedModem)
00524     {
00525         for (int x=0; x<100;x++)
00526         {
00527             DBG("Trying to connect the dongle");
00528             m_dongle.tryConnect();
00529             if (m_dongle.connected())
00530             {
00531                 DBG("Great the dongle is connected - I've tried %d times to connect", x);
00532                 detectConnectedModem = true;        // OK we can break out this while loop now - the dongle has been connected
00533                 break;                              // Break out of the for loop once the dongle is connected - otherwise try for a while more
00534             }
00535             Thread::wait(1000);
00536         }
00537         if (!detectConnectedModem)
00538         {
00539             // OK we got this far - so give up trying and let someone know you can't see the modem
00540             m_dongleConnected = false;                  // set the member variable of this object to false - so if we get called again we know we have to try to detect again
00541             ERR("There is no dongle pluged into the board, or the module does not respond. Is the module/modem switched on?");
00542             Thread:wait(1000);
00543             //DBG("Last ditch attempt to re-initialise the USB Subsystem");
00544             //m_dongle.init();
00545             return HARDWARE_NO_RESPONSE;
00546         }
00547     }
00548         
00549     
00550     //while( !m_dongle.connected() )
00551     //{
00552     //  DBG("Trying to connect the dongle");
00553     //  m_dongle.tryConnect();
00554     //  Thread::wait(10);
00555     //}
00556     
00557     
00558   }
00559 
00560   if(m_atOpen)
00561   {
00562     return OK;
00563   }
00564   
00565   DBG("Starting AT thread if needed");
00566   int ret = m_at.open();
00567   if(ret)
00568   {
00569     return ret;
00570   }
00571   
00572   DBG("Sending initialisation commands");
00573   ret = m_at.init();
00574   if(ret)
00575   {
00576     return ret;
00577   }
00578 
00579   if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_VODAFONEK3770)
00580   {
00581     INFO("Using a Vodafone K3770 Dongle");
00582     #if USE_ONE_PORT
00583     DBG("Configuring unsolicited result codes support properly");
00584     //Configuring port to enable 3GPP-compliant unsollicited response codes but disable Huawei-specific unsollicited response codes
00585     ret = m_at.executeSimple("AT^CURC=0;^PORTSEL=1", NULL); //Huawei-specific, not 3GPP-compliant
00586     if(ret != OK)
00587     {
00588       return NET_PROTOCOL;
00589     }
00590     #else
00591     //Configuring port to disable Huawei-specific unsollicited response codes
00592     ret = m_at.executeSimple("AT^CURC=0", NULL); //Huawei-specific, not 3GPP-compliant
00593     if(ret != OK)
00594     {
00595       return NET_PROTOCOL;
00596     }  
00597     #endif
00598   }
00599   else if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_VODAFONEK3772Z)
00600   {
00601     INFO("Using a Vodafone K3772-Z Dongle");
00602     //FIXME this returns %USBMODEM: [0] MODEM DRIVER<CR><LF><CR><LF><CR><LF>OK<CR><LF> which is not a compliant response
00603     /*
00604     //Configuring modem to directly boot into modem mode
00605     ret = m_at.executeSimple("AT%USBMODEM=0", NULL); //Icera-specific, not 3GPP-compliant
00606     if(ret != OK)
00607     {
00608       return NET_PROTOCOL;
00609     }
00610     */
00611   }
00612   else if(m_dongle.getDongleType()== WAN_DONGLE_TYPE_VODAFONEK3773)
00613   {
00614      INFO("Using a Vodafone K3773 M2M dongle.");
00615      INFO("Disabling sim toolkit notifications");
00616      ret = m_at.executeSimple("at^stsf=0",NULL);
00617      if(ret!=0) {
00618         return NET_PROTOCOL;
00619      }
00620      INFO("Disabling other unsolicited notifications");
00621      ret = m_at.executeSimple("at^curc=0",NULL);
00622      if(ret!=0) {
00623         return NET_PROTOCOL;
00624      }
00625   }
00626   else if(m_dongle.getDongleType()== WAN_DONGLE_TYPE_VODAFONEMU509)
00627   {
00628      INFO("Using a Huawei MU509 module.");
00629      m_ppp.setHangupViaATPort(true);
00630      
00631      // this modem defaults to sending a delivery receipt to the SM storage area
00632      // need to disable this for the current library to work in a sensible manner
00633      INFO("Disabling SMS delivery receipts"); 
00634      ret = m_at.executeSimple("at+csmp=1,,0,0",NULL);
00635      if(ret!=0) {
00636         return NET_PROTOCOL;
00637      }
00638      INFO("Disabling unsolicitied notifications");
00639      ret = m_at.executeSimple("AT^CURC=0", NULL); //Huawei-specific, not 3GPP-compliant
00640      if(ret != OK)
00641      {
00642       return NET_PROTOCOL;
00643      } 
00644   }
00645   else
00646   {
00647     WARN("Using an Unknown Dongle");
00648   }
00649 
00650   ATCommandsInterface::ATResult result;
00651 
00652   //Wait for network registration
00653   CREGProcessor cregProcessor;
00654   do
00655   {
00656     DBG("Waiting for network registration");
00657     ret = m_at.execute("AT+CREG?", &cregProcessor, &result);
00658     DBG("Result of command: Err code=%d\n", ret);
00659     DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
00660     if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING)
00661     {
00662       Thread::wait(3000);
00663     }
00664   } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
00665   if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED)
00666   {
00667     ERR("Registration denied");
00668     return NET_AUTH;
00669   }
00670 
00671   m_atOpen = true;
00672 
00673   return OK;
00674 }
00675 
00676 int VodafoneUSBModem::cleanup()
00677 {
00678   if(m_ppp.isConnected())
00679   {
00680     WARN("Data connection is still open"); //Try to encourage good behaviour from the user
00681     m_ppp.disconnect(); 
00682   }
00683   
00684   m_smsInit = false;
00685   m_ussdInit = false;
00686   m_linkMonitorInit = false;
00687   //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once
00688   
00689   if(m_atOpen)
00690   {
00691     m_at.close();
00692     m_atOpen = false;
00693   }
00694   
00695   m_dongle.disconnect();
00696   m_dongleConnected = false;
00697   
00698   return OK;
00699 }
00700