UbloxUSBGSMModem.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /* UbloxUSBGSMModem.cpp */
  2. /* Copyright (C) 2012 mbed.org, MIT License
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
  5. * and associated documentation files (the "Software"), to deal in the Software without restriction,
  6. * including without limitation the rights to use, copy, modify, merge, publish, distribute,
  7. * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in all copies or
  11. * substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
  14. * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  16. * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. */
  19. #define __DEBUG__ 3
  20. #ifndef __MODULE__
  21. #define __MODULE__ "UbloxUSBGSMModem.cpp"
  22. #endif
  23. #include "core/fwk.h"
  24. #include "UbloxUSBGSMModem.h"
  25. #include "UbloxGSMModemInitializer.h"
  26. #include "USBHost.h"
  27. UbloxUSBGSMModem::UbloxUSBGSMModem(PinName powerGatingPin /*= NC*/, bool powerGatingOnWhenPinHigh /* = true*/) :
  28. m_dongle(), // Construct WANDongle: USB interface with two serial channels to the modem (USBSerialStream objects)
  29. m_atStream(m_dongle.getSerial(1)), // AT commands are sent down one serial channel.
  30. m_pppStream(m_dongle.getSerial(0)), // PPP connections are managed via another serial channel.
  31. m_at(&m_atStream), // Construct ATCommandsInterface with the AT serial channel
  32. m_sms(&m_at), // Construct SMSInterface with the ATCommandsInterface
  33. m_ussd(&m_at), // Construct USSDInterface with the ATCommandsInterface
  34. m_linkMonitor(&m_at), // Construct LinkMonitor with the ATCommandsInterface
  35. m_ppp(&m_pppStream), // Construct PPPIPInterface with the PPP serial channel
  36. m_dongleConnected(false), // Dongle is initially not ready for anything
  37. m_ipInit(false), // PPIPInterface connection is initially down
  38. m_smsInit(false), // SMSInterface starts un-initialised
  39. m_ussdInit(false), // USSDInterface starts un-initialised
  40. m_linkMonitorInit(false), // LinkMonitor subsystem starts un-initialised
  41. m_atOpen(false), // ATCommandsInterface starts in a closed state
  42. m_powerGatingPin(powerGatingPin), // set power gating pin
  43. m_powerGatingOnWhenPinHigh(powerGatingOnWhenPinHigh) // set state semantics for power gating pin
  44. {
  45. USBHost* host = USBHost::getHostInst();
  46. m_dongle.addInitializer(new UbloxGSMModemInitializer(host));
  47. if( m_powerGatingPin != NC )
  48. {
  49. power(false); //Dongle will have to be powered on manually
  50. }
  51. }
  52. class CREGProcessor : public IATCommandsProcessor
  53. {
  54. public:
  55. CREGProcessor() : status(STATUS_REGISTERING)
  56. {
  57. }
  58. enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
  59. REGISTERING_STATUS getStatus()
  60. {
  61. return status;
  62. }
  63. private:
  64. virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
  65. {
  66. int r;
  67. if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
  68. {
  69. switch(r)
  70. {
  71. case 1:
  72. case 5:
  73. status = STATUS_OK;
  74. break;
  75. case 0:
  76. case 2:
  77. status = STATUS_REGISTERING;
  78. break;
  79. case 3:
  80. default:
  81. status = STATUS_FAILED;
  82. break;
  83. }
  84. }
  85. return OK;
  86. }
  87. virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
  88. {
  89. return OK;
  90. }
  91. volatile REGISTERING_STATUS status;
  92. };
  93. #if 0
  94. class COPSProcessor : public IATCommandsProcessor
  95. {
  96. public:
  97. COPSProcessor() : valid(false)
  98. {
  99. network[0] = '\0';
  100. apn[0] = '\0';
  101. bearer[0] = '\0';
  102. }
  103. char* getNetwork()
  104. {
  105. return network;
  106. }
  107. char* getAPN()
  108. {
  109. return apn;
  110. }
  111. char* getBearer()
  112. {
  113. return bearer;
  114. }
  115. bool isValid()
  116. {
  117. return valid;
  118. }
  119. private:
  120. virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
  121. {
  122. int networkId;
  123. int bearerId;
  124. int s = sscanf(line, "+COPS: %*d,%*d,\"%d\",%d", &networkId, &bearerId);
  125. if( s == 2 )
  126. {
  127. switch(networkId)
  128. {
  129. case 23415:
  130. strcpy(network, "Vodafone UK");
  131. strcpy(apn, "pp.vodafone.co.uk");
  132. valid = true;
  133. break;
  134. case 20810:
  135. strcpy(network, "SFR FR");
  136. strcpy(apn, "websfr");
  137. valid = true;
  138. break;
  139. default:
  140. break;
  141. }
  142. }
  143. else
  144. {
  145. return OK;
  146. }
  147. switch(bearerId)
  148. {
  149. case 0: strcpy(bearer, "GSM"); break;
  150. case 1: strcpy(bearer, "GSM Compact"); break;
  151. case 2: strcpy(bearer, "UTRAN"); break;
  152. case 3: strcpy(bearer, "GSM w/EGPRS"); break;
  153. case 4: strcpy(bearer, "UTRAN w/HSDPA"); break;
  154. case 5: strcpy(bearer, "UTRAN w/HSUPA"); break;
  155. case 6: strcpy(bearer, "UTRAN w/HSDPA and HSUPA"); break;
  156. case 7: strcpy(bearer, "E-UTRAN"); break;
  157. default:
  158. break;
  159. }
  160. return OK;
  161. }
  162. virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
  163. {
  164. return OK;
  165. }
  166. char network[24];
  167. char bearer[24];
  168. char apn[24];
  169. volatile bool valid;
  170. };
  171. #endif
  172. int UbloxUSBGSMModem::connect(const char* apn, const char* user, const char* password)
  173. {
  174. if( !m_ipInit )
  175. {
  176. m_ipInit = true;
  177. m_ppp.init();
  178. }
  179. m_ppp.setup(user, password, DEFAULT_MSISDN_GSM);
  180. int ret = init();
  181. if(ret)
  182. {
  183. return ret;
  184. }
  185. #if USE_ONE_PORT
  186. m_smsInit = false; //SMS status reset
  187. m_ussdInit = false; //USSD status reset
  188. m_linkMonitorInit = false; //Link monitor status reset
  189. #endif
  190. ATCommandsInterface::ATResult result;
  191. #if 0
  192. //Get network info & select corresponding APN
  193. COPSProcessor copsProcessor;
  194. DBG("Get network info & select APN from DB");
  195. ret = m_at.execute("AT+COPS=,2;+COPS?", &copsProcessor, &result); //Configure to get operator's info in numeric code & get operator's id
  196. DBG("Result of command: Err code=%d", ret);
  197. DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
  198. if(!copsProcessor.isValid())
  199. {
  200. WARN("Connected to an unknown network, try to connect with default parameters");
  201. DBG("Connected with %s", copsProcessor.getBearer());
  202. }
  203. else
  204. {
  205. DBG("Connected to %s with %s", copsProcessor.getNetwork(), copsProcessor.getBearer());
  206. char cmd[48];
  207. int tries = 3;
  208. sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", copsProcessor.getAPN());
  209. do //Try 3 times because for some reasons it can fail with the K3772-Z dongle
  210. {
  211. ret = m_at.executeSimple(cmd, &result);
  212. DBG("Result of command: Err code=%d", ret);
  213. } while(ret && --tries);
  214. DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
  215. DBG("APN set to %s", copsProcessor.getAPN());
  216. }
  217. #else
  218. if(apn != NULL)
  219. {
  220. char cmd[48];
  221. int tries = 30;
  222. sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
  223. do //Try 30 times because for some reasons it can fail *a lot* with the K3772-Z dongle
  224. {
  225. ret = m_at.executeSimple(cmd, &result);
  226. DBG("Result of command: Err code=%d", ret);
  227. if(ret)
  228. {
  229. Thread::wait(500);
  230. }
  231. } while(ret && --tries);
  232. DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
  233. DBG("APN set to %s", apn);
  234. }
  235. #endif
  236. //Connect
  237. DBG("Connecting");
  238. #if 0
  239. ret = m_at.executeSimple("ATDT *99#", &result);
  240. DBG("Result of command: Err code=%d", ret);
  241. DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
  242. #endif
  243. #if USE_ONE_PORT
  244. m_at.close(); // Closing AT parser
  245. m_atOpen = false; //Will need to be reinitialized afterwards
  246. #endif
  247. #if 0
  248. DBG("AT Parser closed");
  249. if( (ret!=NET_MOREINFO) || (result.result != ATCommandsInterface::ATResult::AT_CONNECT))
  250. {
  251. ERR("Could not connect");
  252. return ret; //Could not connect
  253. }
  254. #endif
  255. DBG("Connecting PPP");
  256. ret = m_ppp.connect();
  257. DBG("Result of connect: Err code=%d", ret);
  258. return ret;
  259. }
  260. int UbloxUSBGSMModem::disconnect()
  261. {
  262. DBG("Disconnecting from PPP");
  263. int ret = m_ppp.disconnect();
  264. if(ret)
  265. {
  266. ERR("Disconnect returned %d, still trying to disconnect", ret);
  267. }
  268. //Ugly but leave dongle time to recover
  269. Thread::wait(500);
  270. #if USE_ONE_PORT
  271. ATCommandsInterface::ATResult result;
  272. DBG("Starting AT thread");
  273. ret = m_at.open();
  274. if(ret)
  275. {
  276. return ret;
  277. }
  278. #endif
  279. DBG("Trying to hangup");
  280. #if 0 //Does not appear to work
  281. int tries = 10;
  282. do
  283. {
  284. ret = m_at.executeSimple("+++", &result, 1000);
  285. DBG("Result of command: Err code=%d\n", ret);
  286. DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
  287. } while(tries-- && ret);
  288. if(!ret)
  289. {
  290. ret = m_at.executeSimple("ATH", &result);
  291. DBG("Result of command: Err code=%d\n", ret);
  292. DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
  293. }
  294. #endif
  295. #if USE_ONE_PORT
  296. //Reinit AT parser
  297. ret = m_at.init();
  298. DBG("Result of command: Err code=%d\n", ret);
  299. if(ret)
  300. {
  301. m_at.close(); // Closing AT parser
  302. DBG("AT Parser closed, could not complete disconnection");
  303. return NET_TIMEOUT;
  304. }
  305. #if 0
  306. m_at.close(); // Closing AT parser
  307. DBG("AT Parser closed");
  308. #endif
  309. #endif
  310. return OK;
  311. }
  312. int UbloxUSBGSMModem::sendSM(const char* number, const char* message)
  313. {
  314. int ret = init();
  315. if(ret)
  316. {
  317. return ret;
  318. }
  319. if(!m_smsInit)
  320. {
  321. ret = m_sms.init();
  322. if(ret)
  323. {
  324. return ret;
  325. }
  326. m_smsInit = true;
  327. }
  328. ret = m_sms.send(number, message);
  329. if(ret)
  330. {
  331. return ret;
  332. }
  333. return OK;
  334. }
  335. int UbloxUSBGSMModem::getSM(char* number, char* message, size_t maxLength)
  336. {
  337. int ret = init();
  338. if(ret)
  339. {
  340. return ret;
  341. }
  342. if(!m_smsInit)
  343. {
  344. ret = m_sms.init();
  345. if(ret)
  346. {
  347. return ret;
  348. }
  349. m_smsInit = true;
  350. }
  351. ret = m_sms.get(number, message, maxLength);
  352. if(ret)
  353. {
  354. return ret;
  355. }
  356. return OK;
  357. }
  358. int UbloxUSBGSMModem::getSMCount(size_t* pCount)
  359. {
  360. int ret = init();
  361. if(ret)
  362. {
  363. return ret;
  364. }
  365. if(!m_smsInit)
  366. {
  367. ret = m_sms.init();
  368. if(ret)
  369. {
  370. return ret;
  371. }
  372. m_smsInit = true;
  373. }
  374. ret = m_sms.getCount(pCount);
  375. if(ret)
  376. {
  377. return ret;
  378. }
  379. return OK;
  380. }
  381. int UbloxUSBGSMModem::sendUSSD(const char* command, char* result, size_t maxLength)
  382. {
  383. int ret = init();
  384. if(ret)
  385. {
  386. return ret;
  387. }
  388. if(!m_ussdInit)
  389. {
  390. ret = m_ussd.init();
  391. if(ret)
  392. {
  393. return ret;
  394. }
  395. m_ussdInit = true;
  396. }
  397. ret = m_ussd.send(command, result, maxLength);
  398. if(ret)
  399. {
  400. return ret;
  401. }
  402. return OK;
  403. }
  404. int UbloxUSBGSMModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
  405. {
  406. int ret = init();
  407. if(ret)
  408. {
  409. return ret;
  410. }
  411. if(!m_linkMonitorInit)
  412. {
  413. ret = m_linkMonitor.init();
  414. if(ret)
  415. {
  416. return ret;
  417. }
  418. m_linkMonitorInit = true;
  419. }
  420. ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
  421. if(ret)
  422. {
  423. return ret;
  424. }
  425. return OK;
  426. }
  427. ATCommandsInterface* UbloxUSBGSMModem::getATCommandsInterface()
  428. {
  429. return &m_at;
  430. }
  431. int UbloxUSBGSMModem::power(bool enable)
  432. {
  433. if( m_powerGatingPin == NC )
  434. {
  435. return NET_INVALID; //A pin name has not been provided in the constructor
  436. }
  437. if(!enable) //Will force components to re-init
  438. {
  439. cleanup();
  440. }
  441. DigitalOut powerGatingOut(m_powerGatingPin);
  442. powerGatingOut = m_powerGatingOnWhenPinHigh?enable:!enable;
  443. return OK;
  444. }
  445. bool UbloxUSBGSMModem::power()
  446. {
  447. if( m_powerGatingPin == NC )
  448. {
  449. return true; //Assume power is always on
  450. }
  451. DigitalOut powerGatingOut(m_powerGatingPin);
  452. return m_powerGatingOnWhenPinHigh?powerGatingOut:!powerGatingOut;
  453. }
  454. int UbloxUSBGSMModem::init()
  455. {
  456. if( !m_dongleConnected )
  457. {
  458. if(!power())
  459. {
  460. //Obviously cannot initialize the dongle if it is disconnected...
  461. ERR("Power is off");
  462. return NET_INVALID;
  463. }
  464. m_dongleConnected = true;
  465. while( !m_dongle.connected() )
  466. {
  467. m_dongle.tryConnect();
  468. Thread::wait(10);
  469. }
  470. }
  471. if(m_atOpen)
  472. {
  473. return OK;
  474. }
  475. DBG("Starting AT thread if needed");
  476. int ret = m_at.open();
  477. if(ret)
  478. {
  479. return ret;
  480. }
  481. DBG("Sending initialisation commands");
  482. ret = m_at.init();
  483. if(ret)
  484. {
  485. return ret;
  486. }
  487. if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200)
  488. {
  489. INFO("Using a u-blox LISA-U");
  490. }
  491. else
  492. {
  493. WARN("Using an Unknown Dongle");
  494. }
  495. ATCommandsInterface::ATResult result;
  496. //Wait for network registration
  497. CREGProcessor cregProcessor;
  498. do
  499. {
  500. DBG("Waiting for network registration");
  501. ret = m_at.execute("AT+CREG?", &cregProcessor, &result);
  502. DBG("Result of command: Err code=%d\n", ret);
  503. DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
  504. if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING)
  505. {
  506. Thread::wait(3000);
  507. }
  508. } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
  509. if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED)
  510. {
  511. ERR("Registration denied");
  512. return NET_AUTH;
  513. }
  514. m_atOpen = true;
  515. return OK;
  516. }
  517. int UbloxUSBGSMModem::cleanup()
  518. {
  519. if(m_ppp.isConnected())
  520. {
  521. WARN("Data connection is still open"); //Try to encourage good behaviour from the user
  522. m_ppp.disconnect();
  523. }
  524. m_smsInit = false;
  525. m_ussdInit = false;
  526. m_linkMonitorInit = false;
  527. //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once
  528. if(m_atOpen)
  529. {
  530. m_at.close();
  531. m_atOpen = false;
  532. }
  533. m_dongle.disconnect();
  534. m_dongleConnected = false;
  535. return OK;
  536. }