psm_demo.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /*================================================================
  2. Copyright (c) 2021, Quectel Wireless Solutions Co., Ltd. All rights reserved.
  3. Quectel Wireless Solutions Proprietary and Confidential.
  4. =================================================================*/
  5. /*=================================================================
  6. EDIT HISTORY FOR MODULE
  7. This section contains comments describing changes made to the module.
  8. Notice that changes are listed in reverse chronological order.
  9. WHEN WHO WHAT, WHERE, WHY
  10. ------------ ------- -------------------------------------------------------------------------------
  11. =================================================================*/
  12. /*===========================================================================
  13. * include files
  14. ===========================================================================*/
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #include "ql_api_osi.h"
  19. #include "ql_log.h"
  20. #include "psm_demo.h"
  21. #include "ql_api_datacall.h"
  22. #include "ql_power.h"
  23. #include "ql_api_rtc.h"
  24. #include "ql_api_dev.h"
  25. #include "ql_api_nw.h"
  26. /*===========================================================================
  27. * Macro Definition
  28. ===========================================================================*/
  29. #define QL_PSMDEMO_LOG_LEVEL QL_LOG_LEVEL_INFO
  30. #define QL_PSMDEMO_LOG(msg, ...) QL_LOG(QL_PSMDEMO_LOG_LEVEL, "ql_PSM", msg, ##__VA_ARGS__)
  31. #define QL_PSMDEMO_LOG_PUSH(msg, ...) QL_LOG_PUSH("ql_PSM", msg, ##__VA_ARGS__)
  32. #define leapyear(year) ((year) % 4 == 0)
  33. #define days_in_year(a) (leapyear(a) ? 366 : 365)
  34. #define days_in_month(a) (month_days[(a) - 1])
  35. #define FEBRUARY 2
  36. #define STARTOFTIME 1970
  37. #define SECDAY 86400L
  38. #define SECYR (SECDAY * 365)
  39. #if !defined(require_action)
  40. #define require_action(x, action, str) \
  41. do \
  42. { \
  43. if(x != 0) \
  44. { \
  45. QL_PSMDEMO_LOG(str); \
  46. {action;} \
  47. } \
  48. } while( 1==0 )
  49. #endif
  50. #define QL_PSMDEMO_SIM_ID 0
  51. #define QL_PSMDEMO_WAKE_UP_TIME 60 //unit:s
  52. /*===========================================================================
  53. * Variate
  54. ===========================================================================*/
  55. ql_task_t psm_task = NULL;
  56. ql_sem_t psm_rtc_update_sem =NULL;
  57. ql_sem_t psm_data_reg_sem = NULL;
  58. static int month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  59. /*===========================================================================
  60. * Functions
  61. ===========================================================================*/
  62. static int ql_rtc_fix_weekday(ql_rtc_time_t *rtc_time)
  63. {
  64. int leapsToDate;
  65. int lastYear;
  66. int day;
  67. int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
  68. if (rtc_time->tm_year < 1753)
  69. return -1;
  70. lastYear=rtc_time->tm_year-1;
  71. //Number of leap corrections to apply up to end of last year
  72. leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
  73. //This year is a leap year if it is divisible by 4 except when it is divisible by 100 unless it is divisible by 400
  74. //e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
  75. if((rtc_time->tm_year%4==0) && ((rtc_time->tm_year%100!=0) || (rtc_time->tm_year%400==0)) && (rtc_time->tm_mon>2))
  76. {
  77. //We are past Feb. 29 in a leap year
  78. day=1;
  79. }
  80. else
  81. {
  82. day=0;
  83. }
  84. day += lastYear*365 + leapsToDate + MonthOffset[rtc_time->tm_mon-1] + rtc_time->tm_mday;
  85. rtc_time->tm_wday=day%7;
  86. return 0;
  87. }
  88. static int ql_sec_conv_rtc_time(int64_t* time_t, ql_rtc_time_t *rtc_time)
  89. {
  90. int i;
  91. long hms, day;
  92. day = *time_t / SECDAY;
  93. hms = *time_t % SECDAY;
  94. //Hours, minutes, seconds are easy
  95. rtc_time->tm_hour = hms / 3600;
  96. rtc_time->tm_min = (hms % 3600) / 60;
  97. rtc_time->tm_sec = (hms % 3600) % 60;
  98. //Number of years in days
  99. for (i = STARTOFTIME; day >= days_in_year(i); i++)
  100. {
  101. day -= days_in_year(i);
  102. }
  103. rtc_time->tm_year = i;
  104. //Number of months in days left
  105. if (leapyear(rtc_time->tm_year))
  106. {
  107. days_in_month(FEBRUARY) = 29;
  108. }
  109. for (i = 1; day >= days_in_month(i); i++)
  110. {
  111. day -= days_in_month(i);
  112. }
  113. days_in_month(FEBRUARY) = 28;
  114. rtc_time->tm_mon = i;
  115. //Days are what is left over (+1) from all that.
  116. rtc_time->tm_mday = day + 1;
  117. //Determine the day of week
  118. return ql_rtc_fix_weekday(rtc_time);
  119. }
  120. static int ql_rtc_conv_sec_time(int64_t* time_t, ql_rtc_time_t* rtc_time)
  121. {
  122. int mon = rtc_time->tm_mon;
  123. int year = rtc_time->tm_year;
  124. int64_t days, hours;
  125. mon -= 2;
  126. if (0 >= (int)mon)
  127. {
  128. // 1..12 -> 11,12,1..10
  129. mon += 12;
  130. //Puts Feb last since it has leap day
  131. year -= 1;
  132. }
  133. days = (unsigned long)(year / 4 - year / 100 + year / 400 +367 * mon / 12 + rtc_time->tm_mday) + year * 365 - 719499;
  134. hours = (days * 24) + rtc_time->tm_hour;
  135. *time_t = (hours * 60 + rtc_time->tm_min) * 60 + rtc_time->tm_sec;
  136. return 0;
  137. }
  138. void ql_psm_nw_notify_cb(uint8_t sim_id, unsigned int ind_type, void *ind_msg_buf)
  139. {
  140. switch(ind_type)
  141. {
  142. case QUEC_NW_NITZ_TIME_UPDATE_IND:
  143. {
  144. QL_PSMDEMO_LOG("time update!");
  145. ql_rtos_semaphore_release(psm_rtc_update_sem);
  146. break;
  147. }
  148. case QUEC_NW_DATA_REG_STATUS_IND:
  149. {
  150. ql_nw_common_reg_status_info_s *data_reg_status=(ql_nw_common_reg_status_info_s *)ind_msg_buf;
  151. QL_PSMDEMO_LOG("Sim%d data reg status changed, current status is %d", sim_id, data_reg_status->state);
  152. if((QL_NW_REG_STATE_HOME_NETWORK == data_reg_status->state) || (QL_NW_REG_STATE_ROAMING == data_reg_status->state))
  153. {
  154. ql_rtos_semaphore_release(psm_data_reg_sem);
  155. }
  156. break;
  157. }
  158. }
  159. }
  160. void ql_psm_cb(void *ctx)
  161. {
  162. return;
  163. }
  164. static bool ql_psm_set_cfun(void)
  165. {
  166. uint8_t cfun = 0;
  167. int ret = 0;
  168. if(ql_dev_get_modem_fun(&cfun, QL_PSMDEMO_SIM_ID) != QL_DEV_SUCCESS)
  169. {
  170. return false;
  171. }
  172. QL_PSMDEMO_LOG("cfun: %d", cfun);
  173. if(QL_DEV_CFUN_MIN == cfun)
  174. {
  175. return true;
  176. }
  177. ret = ql_dev_set_modem_fun(QL_DEV_CFUN_AIR, 0, QL_PSMDEMO_SIM_ID);
  178. if(ret != QL_DEV_SUCCESS)
  179. {
  180. QL_PSMDEMO_LOG("set QL_DEV_CFUN_AIR, ret: 0x%x", ret);
  181. return false;
  182. }
  183. ret = ql_dev_set_modem_fun(QL_DEV_CFUN_FULL, 0, QL_PSMDEMO_SIM_ID);
  184. if(ret != QL_DEV_SUCCESS)
  185. {
  186. QL_PSMDEMO_LOG("set QL_DEV_CFUN_FULL, ret: 0x%x", ret);
  187. return false;
  188. }
  189. return true;
  190. }
  191. static bool ql_psm_set_alarm(void)
  192. {
  193. int ret = 0;
  194. int64_t time_sec=0;
  195. ql_rtc_time_t tm={0};
  196. ret = ql_rtc_get_time(&tm);
  197. if(ret != QL_RTC_SUCCESS)
  198. {
  199. QL_PSMDEMO_LOG("get time err");
  200. return false;
  201. }
  202. ql_rtc_print_time(tm);
  203. ql_rtc_conv_sec_time(&time_sec,&tm);
  204. time_sec += QL_PSMDEMO_WAKE_UP_TIME;
  205. ql_sec_conv_rtc_time(&time_sec,&tm);
  206. ret = ql_rtc_set_alarm(&tm);
  207. if(ret != QL_RTC_SUCCESS)
  208. {
  209. QL_PSMDEMO_LOG("set alarm err");
  210. return false;
  211. }
  212. ql_rtc_print_time(tm);
  213. //Enable RTC alarm
  214. ret = ql_rtc_enable_alarm(1);
  215. if(ret != QL_RTC_SUCCESS)
  216. {
  217. QL_PSMDEMO_LOG("enable alarm err");
  218. return false;
  219. }
  220. return true;
  221. }
  222. static void ql_psm_demo_thread(void *param)
  223. {
  224. uint8_t source = 0;
  225. int ret = 0,retry_count = 0;
  226. int profile_idx = 1; //range 1 to 7
  227. ql_nw_reg_status_info_s* nw_info = (ql_nw_reg_status_info_s*)calloc(1,sizeof(ql_nw_reg_status_info_s));
  228. ql_data_call_info_s* info = (ql_data_call_info_s*)calloc(1,sizeof(ql_data_call_info_s));
  229. if(nw_info == NULL|| info ==NULL)
  230. {
  231. QL_PSMDEMO_LOG("calloc fail!");
  232. goto PSM_EXIT;
  233. }
  234. if(QL_SLEEP_SUCCESS != ql_psm_register_enter_cb(ql_psm_cb,NULL))
  235. {
  236. QL_PSMDEMO_LOG("register psm callback fail!");
  237. goto PSM_EXIT;
  238. }
  239. if(QL_NW_SUCCESS != ql_nw_register_cb(ql_psm_nw_notify_cb))
  240. {
  241. QL_PSMDEMO_LOG("register network callback fail");
  242. goto PSM_EXIT;
  243. }
  244. if(0 == ql_get_powerup_reason(&source) && QL_PWRUP_PSM_WAKEUP == source)
  245. {
  246. QL_PSMDEMO_LOG("PSM WAKEUP!");
  247. ret = ql_get_data_call_info(QL_PSMDEMO_SIM_ID, profile_idx, info);
  248. if(QL_DATACALL_SUCCESS == ret && info->v4.state)
  249. {
  250. QL_PSMDEMO_LOG("info.v4.addr.ip: %s", ip4addr_ntoa(&(info->v4.addr.ip)));
  251. }
  252. else{
  253. QL_PSMDEMO_LOG("ql_get_data_call_info fail!");
  254. goto PSM_EXIT;
  255. }
  256. //Reset alarm for PSM wake up
  257. if(!ql_psm_set_alarm())
  258. {
  259. QL_PSMDEMO_LOG("alarm set fail!");
  260. goto PSM_EXIT;
  261. }
  262. }
  263. else{
  264. //Enable PSM when first power on
  265. char *periodic_TAU_time="00100001"; //T3412
  266. char *active_time="00000001"; //T3324
  267. if(QL_SLEEP_SUCCESS == ql_psm_sleep_enable(QL_PSMDEMO_SIM_ID, true, periodic_TAU_time, active_time))
  268. {
  269. ret = ql_rtos_semaphore_create(&psm_rtc_update_sem, 0);
  270. if(ret != QL_OSI_SUCCESS)
  271. {
  272. QL_PSMDEMO_LOG("psm_rtc_update_sem created failed, ret = 0x%x", ret);
  273. goto PSM_EXIT;
  274. }
  275. ret = ql_rtos_semaphore_create(&psm_data_reg_sem, 0);
  276. if(ret != QL_OSI_SUCCESS)
  277. {
  278. QL_PSMDEMO_LOG("ql_data_reg_sem created failed, ret = 0x%x", ret);
  279. goto PSM_EXIT;
  280. }
  281. //Call a necessary delay to avoid failure of getting cfun status when power on.
  282. ql_rtos_task_sleep_ms(2000);
  283. if(!ql_psm_set_cfun())
  284. {
  285. QL_PSMDEMO_LOG("cfun set fail!");
  286. goto PSM_EXIT;
  287. }
  288. if(ql_rtos_semaphore_wait(psm_rtc_update_sem, 10000))
  289. {
  290. QL_PSMDEMO_LOG("psm_rtc_update_sem time out");
  291. }
  292. //PDP active
  293. ret = ql_nw_get_reg_status(QL_PSMDEMO_SIM_ID, nw_info);
  294. if((QL_NW_REG_STATE_HOME_NETWORK != nw_info->data_reg.state) && (QL_NW_REG_STATE_ROAMING != nw_info->data_reg.state))
  295. {
  296. if(ql_rtos_semaphore_wait(psm_data_reg_sem, 10000))
  297. {
  298. QL_PSMDEMO_LOG("ql_data_reg_sem time out");
  299. goto PSM_EXIT;
  300. }
  301. }
  302. ret = ql_set_data_call_asyn_mode(QL_PSMDEMO_SIM_ID, profile_idx, 0); //Set to sync mode
  303. //Try reconnect 10 times, time interval is 20 seconds
  304. while(((ret = ql_start_data_call(QL_PSMDEMO_SIM_ID, profile_idx, QL_PDP_TYPE_IP, NULL, NULL, NULL, 0)) != QL_DATACALL_SUCCESS) && (retry_count < 10))
  305. {
  306. retry_count++;
  307. QL_PSMDEMO_LOG("gprs reconnect fail, the retry count is %d", retry_count);
  308. ql_rtos_task_sleep_ms(20000);
  309. }
  310. if(QL_DATACALL_SUCCESS != ret)
  311. {
  312. goto PSM_EXIT;
  313. }
  314. ret = ql_get_data_call_info(QL_PSMDEMO_SIM_ID, profile_idx, info);
  315. if(QL_DATACALL_SUCCESS == ret && info->v4.state)
  316. {
  317. QL_PSMDEMO_LOG("info.v4.addr.ip: %s", ip4addr_ntoa(&(info->v4.addr.ip)));
  318. }
  319. else{
  320. QL_PSMDEMO_LOG("ql_get_data_call_info fail!");
  321. goto PSM_EXIT;
  322. }
  323. //set alarm
  324. if(!ql_psm_set_alarm())
  325. {
  326. QL_PSMDEMO_LOG("alarm set fail!");
  327. goto PSM_EXIT;
  328. }
  329. QL_PSMDEMO_LOG("psm enable!");
  330. ql_autosleep_enable(true);
  331. }
  332. else
  333. {
  334. QL_PSMDEMO_LOG("psm enable fail!");
  335. goto PSM_EXIT;
  336. }
  337. }
  338. PSM_EXIT:
  339. if(nw_info)
  340. {
  341. free(nw_info);
  342. nw_info = NULL;
  343. }
  344. if(info)
  345. {
  346. free(info);
  347. info = NULL;
  348. }
  349. ql_rtos_semaphore_delete(psm_rtc_update_sem);
  350. ql_rtos_semaphore_delete(psm_data_reg_sem);
  351. psm_rtc_update_sem = NULL;
  352. psm_data_reg_sem = NULL;
  353. ql_nw_register_cb(NULL);
  354. ql_rtos_task_delete(NULL);
  355. }
  356. void ql_psm_app_init(void)
  357. {
  358. QlOSStatus err = QL_OSI_SUCCESS;
  359. err = ql_rtos_task_create(&psm_task, 4096, APP_PRIORITY_NORMAL, "ql_psmdemo", ql_psm_demo_thread, NULL, 3);
  360. if(err != QL_OSI_SUCCESS)
  361. {
  362. QL_PSMDEMO_LOG("psm demo task created failed", err);
  363. return;
  364. }
  365. return;
  366. }