4 * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Hayoon Ko <hayoon.ko@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
39 #include "s_network.h"
41 #include "atchannel.h"
44 static char s_ATBuffer[MAX_AT_RESPONSE+1];
45 static char *s_ATBufferCur = s_ATBuffer;
47 struct sms_pdu_control
49 gboolean sms_pdu_mode;
58 static struct sms_pdu_control spc;
60 static int s_readCount = 0;
62 enum ATCommandType s_type;
63 char *s_responsePrefix= NULL;
64 struct ATResponse *sp_response= NULL;
67 static const char * s_smsUnsoliciteds[] = {
71 static int isSMSUnsolicited(const char *line)
75 for (i = 0 ; i < NUM_ELEMS(s_smsUnsoliciteds) ; i++) {
76 if (strStartsWith(line, s_smsUnsoliciteds[i])) {
84 static void startSMSBuffering(char* line)
86 char* temp_line = NULL;
89 spc.line1 = strdup(line);
93 at_tok_start(&temp_line);
94 at_tok_nextint(&temp_line, &sms_pdu_len);
96 dbg("total pdu length : %d", sms_pdu_len);
97 spc.sms_pdu_len = sms_pdu_len;
100 spc.ppdu = malloc(sizeof(char) *sms_pdu_len);
101 spc.sms_pdu_mode = TRUE;
105 static void stopSMSBuffering()
107 if(spc.line1 != NULL)
114 spc.sms_pdu_mode = FALSE;
120 spc.ppdu_marker = NULL;
122 dbg("sms pdu data buffering ended!");
125 static void handleFinalResponse(TcoreHal* hal, const char *line)
127 dbg("Final response arrived. call response callback");
129 // 1. add final rsp string into sp_response
130 sp_response->finalResponse = strdup(line);
132 // 1.1 reverse intermediates
133 reverseIntermediates(sp_response);
135 // 2. pop head pending from queue -> call callback hung pending(on_response) ->
136 // release sp_response/s_responsePrefix -> release userRequest/pending -> send next pending from queue
137 // warning) length have no meaning. data always pointer sp_response
138 tcore_hal_dispatch_response_data(hal, ID_RESERVED_AT, strlen(line), sp_response);
141 static void onUnsolicited (const char *s, TcorePlugin* plugin, char* sms_pdu, int pdu_len)
143 char *line = NULL, *p= NULL;
145 struct smsDeliveryPDU smsPdu;
148 int status, direction;
150 #define STATUS_INCOMING 4
151 #define STATUS_WAITING 5
154 if(strStartsWith(s,"+CMT:")){
156 cmd = EVENT_SMS_INCOM_MSG;
158 smsPdu.cmdLine = strdup(s);
159 smsPdu.pdu = malloc(pdu_len);
160 memcpy(smsPdu.pdu, sms_pdu, pdu_len);
161 smsPdu.len = pdu_len;
163 tcore_plugin_core_object_event_emit(plugin, cmd, &smsPdu);
164 free(smsPdu.cmdLine);
169 /* Ignore unsolicited responses until we're initialized.
170 * This is OK because the RIL library will poll for initial state
172 else if (strStartsWith(s,"%SCFUN:")){
173 /* SS specific -- modem power status notification */
174 cmd = EVENT_MODEM_PHONE_STATE;
176 else if(strStartsWith(s,"%SCSIM:")){
177 cmd = EVENT_SIM_PIN_STATUS;
179 else if(strStartsWith(s,"%SCLCC:")){
184 at_tok_nextint(&p, &id);
185 at_tok_nextint(&p, &direction);
186 at_tok_nextint(&p, &status);
189 case STATUS_INCOMING:
190 cmd = EVENT_CALL_INCOMING;
193 cmd = EVENT_CALL_WAITING;
196 cmd = EVENT_CALL_STATUS;
202 dbg("%SCLCC cmd : %d",cmd);
204 else if (strStartsWith(s,"+CRING:")
205 || strStartsWith(s,"RING")){
206 dbg("incoming call notification - wait for SCLCC with status 4");
209 else if (strStartsWith(s,"CONNECT")){
210 dbg("call connect notification - wait for SCLCC with status 0");
213 else if (strStartsWith(s,"NO CARRIER")){
214 dbg("call release notification - wait for SCLCC with status 7");
217 else if(strStartsWith(s,"+CCWA:")){
218 dbg("call waiting notification - wait for SCLCC with status 5");
221 else if (strStartsWith(s,"+CREG:")
222 || strStartsWith(s,"+CGREG:")){
223 cmd = EVENT_NETWORK_REGISTRATION;
225 else if (strStartsWith(s,"+CMGS:")) {
226 cmd = EVENT_SMS_SEND_ACK;
228 else if (strStartsWith(s,"%SCDEV:")) {
229 cmd = EVENT_SMS_DEVICE_READY;
231 else if(strStartsWith(s,"+CIEV:")){
232 cmd = EVENT_NETWORK_ICON_INFO;
234 else if (strStartsWith(s,"+CSSU:")){
237 else if (strStartsWith(s,"+CUSD:")){
245 tcore_plugin_core_object_event_emit(plugin, cmd, line);
251 static void processLine(TcoreHal *hal, char *line, TcorePlugin* p)
253 TcoreQueue* pPendingQueue = NULL;
254 TcorePending* pPending =NULL;
255 gboolean result_status = FALSE;
256 pPendingQueue =(TcoreQueue*)tcore_hal_ref_queue(hal);
257 pPending = (TcorePending*)tcore_queue_ref_head(pPendingQueue); //topmost request
259 dbg("processLine -------start");
261 if(TCORE_RETURN_SUCCESS == tcore_pending_get_send_status(pPending, &result_status)
262 && (result_status == FALSE))//request not sent but data comes - Unsolicited msg!
264 /* no command pending */
265 dbg("no command pending. call onUnsolicited()");
266 onUnsolicited(line, p, NULL, 0);
267 } else if (isFinalResponseSuccess(line)) {
268 dbg("final response -success. call handleFinalResponse()");
269 sp_response->success = 1;
270 handleFinalResponse(hal, line);
271 } else if (isFinalResponseError(line)) {
272 dbg("final response -ERROR. call handleFinalResponse()");
273 sp_response->success = 0;
274 handleFinalResponse(hal, line);
275 } else switch (s_type) {
278 dbg("[NO_RESULT]:call onUnsolicited()");
279 onUnsolicited(line, p, NULL, 0);
284 if (sp_response->p_intermediates == NULL
287 dbg("[NUMERIC]:line[0] is digit. call addIntermediate()");
288 addIntermediate(line);
290 /* either we already have an intermediate response or
291 the line doesn't begin with a digit */
292 dbg("[NUMERIC]:either we already have an intermediate response or the line doesn't begin with a digit. call onUnsolicited()");
293 onUnsolicited(line,p,NULL, 0);
299 if (sp_response->p_intermediates == NULL
300 && strStartsWith (line, s_responsePrefix)
302 dbg("[SINGLELINE]:line starts with s_responsePrefix. call addIntermediate()");
303 addIntermediate(line);
305 /* we already have an intermediate response */
306 dbg("[SINGLELINE]:we already have an intermediate response. call onUnsolicited()");
307 onUnsolicited(line,p, NULL, 0);
312 if (strStartsWith (line, s_responsePrefix)) {
313 dbg("[MULTILINE]:line starts with s_responsePrefix. call addIntermediate()");
314 addIntermediate(line);
316 dbg("[MULTILINE]:line don't starts with s_responsePrefix. call onUnsolicited()");
317 onUnsolicited(line,p, NULL, 0);
321 default: /* this should never be reached */
322 err("Unsupported AT command type %d\n", s_type);
323 onUnsolicited(line,p, NULL, 0);
328 static gboolean readline(TcoreHal *hal, unsigned int data_len, const void *data, TcorePlugin* p)
333 char *p_marker = NULL;
334 int len, leftover_len;
339 act_data = (char*)data;
342 dbg("recv string = %s, length : %d", (char*)act_data, act_len);
343 /* this is a little odd. I use *s_ATBufferCur == 0 to
344 * mean "buffer consumed completely". If it points to a character, than
345 * the buffer continues until a \0
348 /*check sms pdu cumulating process - data hijacking*/
349 if(spc.sms_pdu_mode == TRUE)
350 { //continue sms pdu buffering
351 dbg("resume pdu buffering. pdu size : %d, gathered size : %d",spc.sms_pdu_len,spc.cum_pdu_len);
352 len = spc.sms_pdu_len - spc.cum_pdu_len; //length needed
355 dbg("whole pdu received - data surplus");
356 memcpy(spc.ppdu_marker, act_data,len);//data fully copied
357 spc.cum_pdu_len = spc.cum_pdu_len + len;
359 //change data & datalen
360 act_data = act_data + len;
361 act_len = act_len - len;
362 dbg("recv string changed to = %s, length changed to : %d", (char*)act_data, act_len);
364 onUnsolicited(spc.line1, p, spc.ppdu, spc.sms_pdu_len);
366 dbg("incoming sms handled. back to normal mode & continue");
368 else if(act_len == len){
369 dbg("exactly whole pdu received");
371 memcpy(spc.ppdu_marker, act_data,len);//data fully copied
372 spc.cum_pdu_len = spc.cum_pdu_len + len;
374 onUnsolicited(spc.line1, p, spc.ppdu, spc.sms_pdu_len);
376 dbg("all incoming data consumed. return");
380 dbg("data received but not sufficient");
381 memcpy(spc.ppdu_marker, act_data,act_len);
382 spc.ppdu_marker = spc.ppdu_marker +act_len;
383 spc.cum_pdu_len = spc.cum_pdu_len + act_len;
384 dbg("data buffered. wait for more data");
390 if (*s_ATBufferCur == '\0')
393 s_ATBufferCur = s_ATBuffer;
394 *s_ATBufferCur = '\0';
399 /* *s_ATBufferCur != '\0' */
400 /* there's data in the buffer from the last read */
402 // skip over leading newlines
403 while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n')
406 p_eol = findNextEOL(s_ATBufferCur);
410 /* a partial line. move it up and prepare to read more */
412 len = strlen(s_ATBufferCur);
414 memmove(s_ATBuffer, s_ATBufferCur, len + 1);
415 p_read = s_ATBuffer + len;
416 s_ATBufferCur = s_ATBuffer;
418 /* Otherwise, (p_eol !- NULL) there is a complete line */
421 err("this should not be happening - complete data pending??");
426 if (0 > MAX_AT_RESPONSE - ((p_read - s_ATBuffer)+(int)act_len))
428 dbg("ERROR: Input line exceeded buffer\n");
429 /* ditch buffer and start over again */
430 s_ATBufferCur = s_ATBuffer;
431 *s_ATBufferCur = '\0';
435 //copy data into buffer
436 memcpy(p_read, act_data, act_len);
440 /* read error encountered or EOF reached */
442 err("atchannel: EOF reached");
445 err("invalid data coming");
451 s_readCount += act_len;
452 p_read[act_len] = '\0';
454 p_marker = p_read + act_len; //pin the last position of data copy
460 // skip over leading newlines
461 while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n')
464 p_eol = findNextEOL(s_ATBufferCur);
466 if(p_eol !=NULL) /*end of line found!*/
468 /* a full line in the buffer. Place a \0 over the \r and return */
471 s_ATBufferCur = p_eol + 1; /* this will always be <= p_read, */
472 /* and there will be a \0 at *p_read */
474 dbg("complete line found. process it/n");
475 dbg("rsp line : %s/n",ret);
476 if(1 == isSMSUnsolicited(ret))
478 dbg("start of incoming sms found!!! - next data is PDU");
479 startSMSBuffering(ret);
480 s_ATBufferCur++; //move starting point by 1 - it goes to the very starting point of PDU
481 leftover_len = p_marker - s_ATBufferCur;
483 dbg("count leftover : %d", leftover_len);
485 dbg("pointer address error -serious!");
488 else if(leftover_len ==0){
489 dbg("no pdu received - wait for incoming data");
491 spc.ppdu_marker = spc.ppdu;
493 else if(leftover_len >= spc.sms_pdu_len){
494 dbg("whole pdu already received!");
495 memcpy(spc.ppdu, s_ATBufferCur, spc.sms_pdu_len);
496 spc.cum_pdu_len = spc.sms_pdu_len;
497 onUnsolicited(spc.line1, p, spc.ppdu, spc.sms_pdu_len);
498 s_ATBufferCur = s_ATBufferCur+spc.sms_pdu_len;
499 dbg("move buffercur to point the very end of pdu!");
503 dbg("staring part of pdu received!");
504 memcpy(spc.ppdu, s_ATBufferCur,leftover_len);
505 spc.ppdu_marker = spc.ppdu + leftover_len;
506 spc.cum_pdu_len = leftover_len;
507 s_ATBufferCur = s_ATBufferCur + leftover_len;
513 processLine(hal, ret,p);
518 dbg("complete responses all handled/n");
520 }while(p_eol != NULL);
522 dbg("all the pending rsp's handled. wait for next incoming data/n");
526 static enum tcore_hook_return on_hal_send(TcoreHal *hal, unsigned int data_len, void *data, void *user_data)
528 hook_hex_dump(TX, data_len, data);
529 return TCORE_HOOK_RETURN_CONTINUE;
532 static void on_hal_recv(TcoreHal *hal, unsigned int data_len, const void *data, void *user_data)
534 gboolean ret = FALSE;
535 TcorePlugin *plugin = user_data;
537 ret = readline(hal,data_len, data,plugin);
540 static gboolean on_load()
547 static gboolean on_init(TcorePlugin *p)
556 h = tcore_server_find_hal(tcore_plugin_ref_server(p), "vmodem");
561 tcore_hal_add_send_hook(h, on_hal_send, p);
562 tcore_hal_add_recv_callback(h, on_hal_recv, p);
565 s_network_init(p, h);
572 // s_phonebook_init(p);
573 #ifndef TEST_AT_SOCKET
574 tcore_hal_set_power(h, TRUE);
576 //send "CPAS" command to invoke POWER UP NOTI
577 s_modem_send_poweron(p);
582 static void on_unload(TcorePlugin *p)
584 struct global_data *gd;
591 gd = tcore_plugin_ref_user_data(p);
597 struct tcore_plugin_define_desc plugin_define_desc =
600 .priority = TCORE_PLUGIN_PRIORITY_MID,