ec200u.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. #include "ec200u.h"
  2. #include "../swapi/subjects/serial/serial.h"
  3. // 模块名称
  4. static const char MODULE_NAME[] = "EC200U";
  5. #define MAX_CMD_CHARS 65535 // 单条指令最大字符数
  6. #define MAX_ACK_CHARS 65535 // 指令应答最大字符数
  7. #define WAIT_ACK_TIMEOUT 9000 // 等待应答超时时间ms
  8. #define R_SILENT_TIMEOUT 50 // 应答静默超时时间ms
  9. // 关闭命令回显模式
  10. static const char *AT_USE_ECHO = "ATE0" ;
  11. // 获取模块的IMEI号
  12. static const char *AT_GET_IMEI = "AT+CGSN" ;
  13. // 查看网络注册状态
  14. static const char *AT_GET_CREG = "AT+CREG?";
  15. // 获取当前信号强度
  16. static const char *AT_GET_CSQ = "AT+CSQ" ;
  17. // 获取SIM-PIN状态
  18. static const char *AT_GET_PINSTA = "AT+CPIN?";
  19. // 获取SIM-ICCID号
  20. static const char *AT_GET_ICCID = "AT+ICCID";
  21. // 激活USB网络模式
  22. static const char *AT_QCFG_CMD1 = "AT+QCFG=\"usbnet\",1";
  23. static const char *AT_QCFG_CMD2 = "AT+qnetdevctl=1,1,1" ;
  24. // AT指令请求和应答
  25. typedef struct
  26. {
  27. // 指令
  28. struct
  29. {
  30. char data[MAX_CMD_CHARS+1]; // 指令数据(字符串)
  31. int len; // 指令长度
  32. } cmd;
  33. // 等待执行结束的超时ms
  34. int timeout;
  35. // 等待执行结束的信号量
  36. void *hEndSgl;
  37. // 应答
  38. struct
  39. {
  40. int ret; // 返回值: >0时执行成功, 表明应答数据的长度; <0时出错(-1超时)
  41. char data[MAX_ACK_CHARS+1]; // 应答数据(字符串)
  42. } ack;
  43. } SATCmdTrans;
  44. // 与模块的串口通讯
  45. typedef struct
  46. {
  47. void *h; // 打开的串口句柄
  48. void *do_cmd_mutex; // 指令执行互斥量, 同一时刻正在执行的指令只能有一条
  49. SATCmdTrans *pTrans; // 执行的指令及其应答结果: 空闲时为空, 工作时不为空
  50. } SEC200UCom;
  51. static SEC200UCom s_myCom;
  52. // 定义AT指令格式符
  53. #define CR '\r' // 回车
  54. #define LF '\n' // 换行
  55. #define CRLF "\r\n" // 回车换行
  56. #define ATOK "\r\nOK\r\n" // 应答成功
  57. #define ATER "ERROR" // 应答失败
  58. // 执行一条指令, 返回: 大于0成功; 小于0失败(-1命令超时)
  59. static int comio_doCmd(SATCmdTrans *pTrans/*要执行的指令(IN&OUT)*/, int timeout/*等待应答的超时时间, 单位是毫秒*/)
  60. {
  61. SEC200UCom *pComIO = &s_myCom; const char *log_prefix = serial_get_log_prefix(pComIO->h);
  62. unsigned long stime, wtime, etime; char *pAtCmdStr; int atCmdLen, ret;
  63. xgettickcount(&stime);
  64. ret = sw_mutex_lock(pComIO->do_cmd_mutex, timeout);
  65. if(ret != 0) { pTrans->ack.ret = -1; goto end_p; } // 互斥量上锁超时
  66. xgettickcount(&etime); wtime = etime - stime;
  67. pTrans->hEndSgl = sw_signal_create();
  68. if(!pTrans->hEndSgl) { pTrans->ack.ret = -2; goto end_p; }
  69. pAtCmdStr = (char *)sw_heap_malloc(pTrans->cmd.len+2);
  70. if(!pAtCmdStr) { pTrans->ack.ret = -3; goto end_p; }
  71. memcpy(pAtCmdStr, pTrans->cmd.data, pTrans->cmd.len);
  72. pAtCmdStr[pTrans->cmd.len] = CR; pAtCmdStr[pTrans->cmd.len+1] = '\0';
  73. atCmdLen = strlen((const char *)pAtCmdStr);
  74. serial_recvThrd_pause(pComIO->h); sw_thrd_delay(THRDWAIT_DELAY); // "pComIO->pTrans"的控制权转移前, 先暂停串口数据的接收
  75. ret = serial_send_data(pComIO->h, (unsigned char *)pAtCmdStr, atCmdLen); // 发送一条指令
  76. sw_heap_free(pAtCmdStr); // 释放AT指令字符串内存
  77. if(ret == atCmdLen)
  78. {
  79. sw_log_trace("[%s] %s send a cmd(%d bytes): %s", MODULE_NAME, log_prefix, pTrans->cmd.len, pTrans->cmd.data);
  80. }
  81. else
  82. {
  83. sw_log_error("[%s] %s failed to send a cmd(%d bytes, ret=%d): %s!!", MODULE_NAME, log_prefix, pTrans->cmd.len, ret, pTrans->cmd.data);
  84. serial_recvThrd_resume(pComIO->h); pTrans->ack.ret = -4; goto end_p;
  85. }
  86. pTrans->timeout = (wtime > timeout ? 0 : (timeout - wtime));
  87. pComIO->pTrans = pTrans; serial_recvThrd_resume(pComIO->h); // 恢复串口数据接收, "pComIO->pTrans"的控制权转移给接收线程.
  88. sw_signal_wait(pTrans->hEndSgl, WAIT_FOREVER); // 等待指令执行结束, "pComIO->pTrans"的控制权收回
  89. end_p:
  90. if(pTrans->hEndSgl) sw_signal_destroy(pTrans->hEndSgl);
  91. pComIO->pTrans = NULL;
  92. sw_mutex_unlock(pComIO->do_cmd_mutex);
  93. return pTrans->ack.ret;
  94. }
  95. // 接收、处理来自EC200U模块的数据报文帧, 串口-线程回调
  96. static int comio_data_recv_proc(unsigned long wParam/*传递打开的串口句柄*/, unsigned long lParam/*保留暂未使用*/)
  97. {
  98. SEC200UCom *pComIO = &s_myCom; void *pSerial = pComIO->h; SATCmdTrans *pTrans = pComIO->pTrans;
  99. const unsigned char *pRecvBuf = serial_get_recv_buffer(pSerial); int nRecvBytes = serial_get_recv_buffer_bytes(pSerial);
  100. const char *log_prefix = serial_get_log_prefix(pSerial); bool bPrintRecvBuf = true;
  101. static unsigned long enteredWaitAckTime = 0, now; static unsigned long enteredSilentTime = 0; static int nSilentRecvBytes = 0;
  102. if(!pTrans) { goto ret_p2; } // 无主动指令时
  103. // 1, 计算命令应答超时
  104. if(enteredWaitAckTime == 0) xgettickcount(&enteredWaitAckTime);
  105. xgettickcount(&now);
  106. // 2, 分析应答回复内容
  107. if(strstr((const char *)pRecvBuf, ATOK) != NULL || strstr((const char *)pRecvBuf, ATER) != NULL)
  108. { // 已应答
  109. if(nRecvBytes > nSilentRecvBytes) { enteredSilentTime = now; nSilentRecvBytes = nRecvBytes; }
  110. if((now-enteredSilentTime) < R_SILENT_TIMEOUT) { goto ret_p3; } // 等待静默期结束(延期多接收一会数据, 避免"内容"在"ATOK"或"ATER"之后)
  111. memcpy(pTrans->ack.data, pRecvBuf, nRecvBytes);
  112. pTrans->ack.ret = nRecvBytes; pTrans->ack.data[pTrans->ack.ret] = '\0'; bPrintRecvBuf = false;
  113. sw_log_trace("[%s] %s received a cmd ack(%d bytes): %s", MODULE_NAME, log_prefix, pTrans->ack.ret, pTrans->ack.data);
  114. goto ret_p1;
  115. }
  116. else if((now-enteredWaitAckTime) >= pTrans->timeout)
  117. { // 已超时
  118. sw_log_error("[%s] %s timeout occurred while waiting for the \"%s\" command's ack!!", MODULE_NAME, log_prefix, pTrans->cmd.data);
  119. pTrans->ack.ret = -1; pTrans->ack.data[0] = '\0';
  120. goto ret_p1;
  121. }
  122. else
  123. { // 继续收
  124. goto ret_p3;
  125. }
  126. // 3, 缓存数据接收控制
  127. ret_p1: // 点亮信号量, 表明本指令执行结束
  128. sw_signal_give(pComIO->pTrans->hEndSgl); enteredWaitAckTime = 0; enteredSilentTime = 0; nSilentRecvBytes = 0;
  129. goto ret_p2;
  130. ret_p2: // 清接收缓存, 重新开始一轮新指令
  131. if(nRecvBytes > 0 && bPrintRecvBuf)
  132. {
  133. serial_printf_recv_buffer(pSerial, LEVEL_TRACE); // 十六进制打印当前串口接收数据缓存
  134. sw_log_warn("[%s] %s discarded %d bytes of meaningless data!", MODULE_NAME, log_prefix, nRecvBytes);
  135. }
  136. if(nRecvBytes > 0) serial_clear_recv_buffer(pSerial);
  137. goto ret_p3;
  138. ret_p3: return 1; // 持续累加的接收新数据
  139. }
  140. // 打开与模块的通讯, 返回: 0成功, <0时失败
  141. int EC200U_ComInit()
  142. {
  143. s_myCom.do_cmd_mutex = sw_mutex_create();
  144. if(!s_myCom.do_cmd_mutex) { sw_log_error("[%s] error to create a mutex!!", MODULE_NAME); return -1; }
  145. s_myCom.pTrans = NULL;
  146. const char *serialName = "/dev/ttyUSB0"; int baudrate = 115200;
  147. const char *parityCheck = "none"; // 无校检
  148. s_myCom.h = serial_open(serialName, baudrate, parityCheck, \
  149. comio_data_recv_proc, comio_data_recv_proc, NULL);
  150. if(!s_myCom.h)
  151. {
  152. sw_log_error("[%s] failed to open the \"%s:%d(%s parity)\" device!!", \
  153. MODULE_NAME, serialName, baudrate, parityCheck);
  154. EC200U_ComExit(); return -1;
  155. }
  156. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  157. strcpy(trans.cmd.data, AT_USE_ECHO); trans.cmd.len = strlen(trans.cmd.data);
  158. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  159. if(!(ret > 0 && strstr(trans.ack.data, ATOK)))
  160. {
  161. sw_log_error("[%s] failed to close echo mode!!", MODULE_NAME);
  162. EC200U_ComExit(); return -2+ret;
  163. }
  164. memset(&trans, 0, sizeof(SATCmdTrans));
  165. strcpy(trans.cmd.data, AT_QCFG_CMD1); trans.cmd.len = strlen(trans.cmd.data);
  166. ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  167. if(!(ret > 0 && strstr(trans.ack.data, ATOK)))
  168. {
  169. sw_log_error("[%s] failed to configure USB network mode!!", MODULE_NAME);
  170. EC200U_ComExit(); return -3+ret;
  171. }
  172. memset(&trans, 0, sizeof(SATCmdTrans));
  173. strcpy(trans.cmd.data, AT_QCFG_CMD2); trans.cmd.len = strlen(trans.cmd.data);
  174. ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  175. if(!(ret > 0 && strstr(trans.ack.data, ATOK)))
  176. {
  177. sw_log_error("[%s] failed to activate USB network mode!!", MODULE_NAME);
  178. EC200U_ComExit(); return -4+ret;
  179. }
  180. return 0;
  181. }
  182. // 关闭与模块的通讯, 返回: 0成功, <0时失败
  183. int EC200U_ComExit()
  184. {
  185. if(s_myCom.h) serial_close(s_myCom.h, WAITTHRD_SAFEEXIT_TIMEOUT);
  186. if(s_myCom.do_cmd_mutex) sw_mutex_destroy(s_myCom.do_cmd_mutex);
  187. memset(&s_myCom, 0, sizeof(s_myCom));
  188. return 0;
  189. }
  190. // 改写sysfs虚拟文件, 返回: 0成功, <0失败
  191. static int sysfs_write(const char *path, const char *value)
  192. {
  193. int fd = open(path, O_WRONLY);
  194. if (fd < 0) return -1;
  195. ssize_t len = write(fd, value, strlen(value));
  196. if(len != strlen(value)) { close(fd); return -2; }
  197. close(fd); return 0;
  198. }
  199. // 控制模块断电重启, 返回: 0成功, <0时失败
  200. // 138脚 -- gpio4.IO[20] (4-1)*32+20 = 116 -- 4G_POWER
  201. int EC200U_Repower()
  202. {
  203. sysfs_write("/sys/class/gpio/unexport", "116"); // 撤销原导出
  204. if(sysfs_write("/sys/class/gpio/export", "116") < 0) return -2; // 重设新导出
  205. if(sysfs_write("/sys/class/gpio/gpio116/direction", "out") < 0) return -3; // 设置为输出
  206. if(sysfs_write("/sys/class/gpio/gpio116/value", "1") < 0) return -4; // 输出高电平-断电
  207. sw_thrd_delay(5000); // 等一段时间-毫秒
  208. if(sysfs_write("/sys/class/gpio/gpio116/value", "0") < 0) return -5; // 输出低电平-上电
  209. return 0;
  210. }
  211. // 获取模块的标识号, 返回: =15成功, <0失败(-1命令超时)
  212. int EC200U_GetIMEI(char buf[16]/*固定长15个十进制数字组成*/)
  213. {
  214. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  215. strcpy(trans.cmd.data, AT_GET_IMEI);
  216. trans.cmd.len = strlen(trans.cmd.data);
  217. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  218. if(ret < 0) return ret; // 执行超时或出错
  219. else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
  220. const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
  221. lsa_p: // 行扫描, 逐行分析应答结果
  222. if((*p) == '\0') goto end_p;
  223. pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
  224. if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
  225. if(pLineE) { lineLen = pLineE - pLineS; }
  226. if(lineLen == 0)
  227. { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
  228. if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
  229. else goto end_p;
  230. }
  231. //// 示例: "359759002514931"
  232. if(lineLen == 15)
  233. {
  234. bool isHex = true;
  235. for(int i = 0; i < lineLen; i++) if(!xisxdigit(pLineS[i])) { isHex = false; break; }
  236. if(isHex) { memcpy(buf, pLineS, lineLen); buf[lineLen] = '\0'; return lineLen; } // 成功读到模块的IMEI号
  237. }
  238. p = pLineE + crlfLen;
  239. goto lsa_p; // 继续分析下一行
  240. end_p:
  241. return -5;
  242. }
  243. // 获取网络注册状态, 返回: >=0成功, <0失败(-1命令超时)
  244. int EC200U_GetCregState()
  245. {
  246. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  247. strcpy(trans.cmd.data, AT_GET_CREG);
  248. trans.cmd.len = strlen(trans.cmd.data);
  249. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  250. if(ret < 0) return ret; // 执行超时或出错
  251. else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
  252. const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
  253. const char *p1, *p2; char statBuf[3]; int statLen;
  254. lsa_p: // 行扫描, 逐行分析应答结果
  255. if((*p) == '\0') goto end_p;
  256. pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
  257. if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
  258. if(pLineE) { lineLen = pLineE - pLineS; }
  259. if(lineLen == 0)
  260. { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
  261. if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
  262. else goto end_p;
  263. }
  264. //// 示例: "+CREG: <n>,<stat>"
  265. //// 说明: <n>注册状态报告模式, <stat>当前网络注册状态
  266. p1 = strstr(pLineS, "+CREG:"); p2 = NULL;
  267. if(p1 && p1 < pLineE) { p1 += 6; p2 = strchr(p1, ','); }
  268. if(p2 && p2 < pLineE)
  269. {
  270. p2++; while(*p2 == 0x20 && p2 < pLineE) { p2++; } // 跳过空格
  271. statLen = (pLineE - p2); if(statLen != 1 && statLen != 2) return -5; // 状态值范围: 1或2个数字
  272. memcpy(statBuf, p2, statLen); statBuf[statLen] = '\0';
  273. bool isDigit = true;
  274. for(int i = 0; i < strlen(statBuf); i++) if(!xisdigit(statBuf[i])) { isDigit = false; break; }
  275. if(isDigit) return atoi(statBuf); // 成功读到模块的网络注册状态值
  276. else return -6;
  277. }
  278. p = pLineE + crlfLen;
  279. goto lsa_p; // 继续分析下一行
  280. end_p:
  281. return -7;
  282. }
  283. // 获取当前信号强度, 返回: >=0成功, <0失败(-1命令超时)
  284. int EC200U_GetRSSIFromCSQ()
  285. {
  286. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  287. strcpy(trans.cmd.data, AT_GET_CSQ);
  288. trans.cmd.len = strlen(trans.cmd.data);
  289. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  290. if(ret < 0) return ret; // 执行超时或出错
  291. else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
  292. const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
  293. const char *p1, *p2; char rssiBuf[3]; int rssiLen;
  294. lsa_p: // 行扫描, 逐行分析应答结果
  295. if((*p) == '\0') goto end_p;
  296. pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
  297. if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
  298. if(pLineE) { lineLen = pLineE - pLineS; }
  299. if(lineLen == 0)
  300. { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
  301. if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
  302. else goto end_p;
  303. }
  304. //// 示例: "+CSQ: 15,99"
  305. //// 说明: 逗号前第一个数值是RSSI的值
  306. p1 = strstr(pLineS, "+CSQ:"); p2 = NULL;
  307. if(p1 && p1 < pLineE) { p1 += 5; p2 = strchr(p1, ','); }
  308. if(p2 && p2 < pLineE)
  309. {
  310. while(*p1 == 0x20 && p1 < p2) { p1++; } // 跳过空格
  311. rssiLen = (p2 - p1); if(rssiLen != 1 && rssiLen != 2) return -5; // 强度值范围: 0-99, 1或2个数字
  312. memcpy(rssiBuf, p1, rssiLen); rssiBuf[rssiLen] = '\0';
  313. bool isDigit = true;
  314. for(int i = 0; i < strlen(rssiBuf); i++) if(!xisdigit(rssiBuf[i])) { isDigit = false; break; }
  315. if(isDigit) return atoi(rssiBuf); // 成功读到模块的接收信号强度值
  316. else return -6;
  317. }
  318. p = pLineE + crlfLen;
  319. goto lsa_p; // 继续分析下一行
  320. end_p:
  321. return -7;
  322. }
  323. // 返回电话卡的状态, 返回: 1已可用, <0失败(-1命令超时)
  324. int EC200U_IsSimCardReady()
  325. {
  326. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  327. strcpy(trans.cmd.data, AT_GET_PINSTA);
  328. trans.cmd.len = strlen(trans.cmd.data);
  329. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  330. if(ret < 0) return ret; // 执行超时或出错
  331. else if(strstr(trans.ack.data, ATOK) && strstr(trans.ack.data, "+CPIN: READY")) return 1;
  332. else return -5;
  333. }
  334. // 获取电话卡标识号, 返回: =20成功, <0失败(-1命令超时)
  335. int EC200U_GetSimICCID(char buf[21]/*一般由20个十进制数字组成*/)
  336. {
  337. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  338. strcpy(trans.cmd.data, AT_GET_ICCID);
  339. trans.cmd.len = strlen(trans.cmd.data);
  340. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  341. if(ret < 0) return ret; // 执行超时或出错
  342. else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
  343. const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
  344. const char *p1, *p2; int iccidLen;
  345. lsa_p: // 行扫描, 逐行分析应答结果
  346. if((*p) == '\0') goto end_p;
  347. pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
  348. if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
  349. if(pLineE) { lineLen = pLineE - pLineS; }
  350. if(lineLen == 0)
  351. { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
  352. if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
  353. else goto end_p;
  354. }
  355. //// 示例: "+ICCID: 89860117831003134201"
  356. p1 = strstr(pLineS, "+ICCID:"); p2 = NULL;
  357. if(p1 && p1 < pLineE) { p1 += 7; p2 = pLineE; }
  358. if(p2)
  359. {
  360. while(*p1 == 0x20 && p1 < p2) { p1++; } // 跳过空格
  361. iccidLen = (p2 - p1); if(iccidLen != 20) return -5; // 长度不对
  362. bool isHex = true;
  363. for(int i = 0; i < iccidLen; i++) if(!xisxdigit(p1[i])) { isHex = false; break; }
  364. if(isHex) { memcpy(buf, p1, iccidLen); buf[iccidLen] = '\0'; return iccidLen; } // 成功读到SIM卡的ICCID号
  365. else return -6;
  366. }
  367. p = pLineE + crlfLen;
  368. goto lsa_p; // 继续分析下一行
  369. end_p:
  370. return -7;
  371. }