blob: f7e86e750c428a12a07b50eb6eafdc00c53854d6 [file] [log] [blame]
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001/* //device/system/reference-ril/reference-ril.c
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include <telephony/ril.h>
19#include <stdio.h>
20#include <assert.h>
21#include <string.h>
22#include <errno.h>
23#include <unistd.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <fcntl.h>
27#include <pthread.h>
28#include <alloca.h>
29#include "atchannel.h"
30#include "at_tok.h"
31#include "misc.h"
32#include <getopt.h>
33#include <sys/socket.h>
34#include <cutils/sockets.h>
35#include <termios.h>
David 'Digit' Turneraf1298d2011-02-04 13:36:47 +010036#include <sys/system_properties.h>
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080037
38#define LOG_TAG "RIL"
39#include <utils/Log.h>
40
41#define MAX_AT_RESPONSE 0x1000
42
Wink Savillef4c4d362009-04-02 01:37:03 -070043/* pathname returned from RIL_REQUEST_SETUP_DATA_CALL / RIL_REQUEST_SETUP_DEFAULT_PDP */
Robert Greenwaltf838ede2010-07-15 18:54:53 -070044#define PPP_TTY_PATH "eth0"
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080045
46#ifdef USE_TI_COMMANDS
47
48// Enable a workaround
49// 1) Make incoming call, do not answer
50// 2) Hangup remote end
51// Expected: call should disappear from CLCC line
52// Actual: Call shows as "ACTIVE" before disappearing
53#define WORKAROUND_ERRONEOUS_ANSWER 1
54
55// Some varients of the TI stack do not support the +CGEV unsolicited
56// response. However, they seem to send an unsolicited +CME ERROR: 150
57#define WORKAROUND_FAKE_CGEV 1
58#endif
59
John Wang309ac292009-07-30 14:53:23 -070060typedef enum {
61 SIM_ABSENT = 0,
62 SIM_NOT_READY = 1,
63 SIM_READY = 2, /* SIM_READY means the radio state is RADIO_STATE_SIM_READY */
64 SIM_PIN = 3,
65 SIM_PUK = 4,
66 SIM_NETWORK_PERSONALIZATION = 5
David 'Digit' Turneraf1298d2011-02-04 13:36:47 +010067} SIM_Status;
John Wang309ac292009-07-30 14:53:23 -070068
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080069static void onRequest (int request, void *data, size_t datalen, RIL_Token t);
70static RIL_RadioState currentState();
71static int onSupports (int requestCode);
72static void onCancel (RIL_Token t);
73static const char *getVersion();
74static int isRadioOn();
John Wang309ac292009-07-30 14:53:23 -070075static SIM_Status getSIMStatus();
Wink Savillef6aa7c12009-04-30 14:20:52 -070076static int getCardStatus(RIL_CardStatus **pp_card_status);
77static void freeCardStatus(RIL_CardStatus *p_card_status);
Wink Savillef4c4d362009-04-02 01:37:03 -070078static void onDataCallListChanged(void *param);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080079
80extern const char * requestToString(int request);
81
82/*** Static Variables ***/
83static const RIL_RadioFunctions s_callbacks = {
84 RIL_VERSION,
85 onRequest,
86 currentState,
87 onSupports,
88 onCancel,
89 getVersion
90};
91
92#ifdef RIL_SHLIB
93static const struct RIL_Env *s_rilenv;
94
95#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)
96#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)
97#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)
98#endif
99
100static RIL_RadioState sState = RADIO_STATE_UNAVAILABLE;
101
102static pthread_mutex_t s_state_mutex = PTHREAD_MUTEX_INITIALIZER;
103static pthread_cond_t s_state_cond = PTHREAD_COND_INITIALIZER;
104
105static int s_port = -1;
106static const char * s_device_path = NULL;
107static int s_device_socket = 0;
108
109/* trigger change to this with s_state_cond */
110static int s_closed = 0;
111
112static int sFD; /* file desc of AT channel */
113static char sATBuffer[MAX_AT_RESPONSE+1];
114static char *sATBufferCur = NULL;
115
116static const struct timeval TIMEVAL_SIMPOLL = {1,0};
117static const struct timeval TIMEVAL_CALLSTATEPOLL = {0,500000};
118static const struct timeval TIMEVAL_0 = {0,0};
119
120#ifdef WORKAROUND_ERRONEOUS_ANSWER
121// Max number of times we'll try to repoll when we think
122// we have a AT+CLCC race condition
123#define REPOLL_CALLS_COUNT_MAX 4
124
125// Line index that was incoming or waiting at last poll, or -1 for none
126static int s_incomingOrWaitingLine = -1;
127// Number of times we've asked for a repoll of AT+CLCC
128static int s_repollCallsCount = 0;
129// Should we expect a call to be answered in the next CLCC?
130static int s_expectAnswer = 0;
131#endif /* WORKAROUND_ERRONEOUS_ANSWER */
132
133static void pollSIMState (void *param);
134static void setRadioState(RIL_RadioState newState);
135
136static int clccStateToRILState(int state, RIL_CallState *p_state)
137
138{
139 switch(state) {
140 case 0: *p_state = RIL_CALL_ACTIVE; return 0;
141 case 1: *p_state = RIL_CALL_HOLDING; return 0;
142 case 2: *p_state = RIL_CALL_DIALING; return 0;
143 case 3: *p_state = RIL_CALL_ALERTING; return 0;
144 case 4: *p_state = RIL_CALL_INCOMING; return 0;
145 case 5: *p_state = RIL_CALL_WAITING; return 0;
146 default: return -1;
147 }
148}
149
150/**
151 * Note: directly modified line and has *p_call point directly into
152 * modified line
153 */
Wink Saville3d54e742009-05-18 18:00:44 -0700154static int callFromCLCCLine(char *line, RIL_Call *p_call)
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800155{
156 //+CLCC: 1,0,2,0,0,\"+18005551212\",145
157 // index,isMT,state,mode,isMpty(,number,TOA)?
158
159 int err;
160 int state;
161 int mode;
162
163 err = at_tok_start(&line);
164 if (err < 0) goto error;
165
166 err = at_tok_nextint(&line, &(p_call->index));
167 if (err < 0) goto error;
168
169 err = at_tok_nextbool(&line, &(p_call->isMT));
170 if (err < 0) goto error;
171
172 err = at_tok_nextint(&line, &state);
173 if (err < 0) goto error;
174
175 err = clccStateToRILState(state, &(p_call->state));
176 if (err < 0) goto error;
177
178 err = at_tok_nextint(&line, &mode);
179 if (err < 0) goto error;
180
181 p_call->isVoice = (mode == 0);
182
183 err = at_tok_nextbool(&line, &(p_call->isMpty));
184 if (err < 0) goto error;
185
186 if (at_tok_hasmore(&line)) {
187 err = at_tok_nextstr(&line, &(p_call->number));
188
189 /* tolerate null here */
190 if (err < 0) return 0;
191
192 // Some lame implementations return strings
193 // like "NOT AVAILABLE" in the CLCC line
194 if (p_call->number != NULL
195 && 0 == strspn(p_call->number, "+0123456789")
196 ) {
197 p_call->number = NULL;
198 }
199
200 err = at_tok_nextint(&line, &p_call->toa);
201 if (err < 0) goto error;
202 }
203
Wink Saville74fa3882009-12-22 15:35:41 -0800204 p_call->uusInfo = NULL;
205
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800206 return 0;
207
208error:
209 LOGE("invalid CLCC line\n");
210 return -1;
211}
212
213
214/** do post-AT+CFUN=1 initialization */
215static void onRadioPowerOn()
216{
217#ifdef USE_TI_COMMANDS
218 /* Must be after CFUN=1 */
219 /* TI specific -- notifications for CPHS things such */
220 /* as CPHS message waiting indicator */
221
222 at_send_command("AT%CPHS=1", NULL);
223
224 /* TI specific -- enable NITZ unsol notifs */
225 at_send_command("AT%CTZV=1", NULL);
226#endif
227
228 pollSIMState(NULL);
229}
230
231/** do post- SIM ready initialization */
232static void onSIMReady()
233{
234 at_send_command_singleline("AT+CSMS=1", "+CSMS:", NULL);
235 /*
236 * Always send SMS messages directly to the TE
237 *
238 * mode = 1 // discard when link is reserved (link should never be
239 * reserved)
240 * mt = 2 // most messages routed to TE
241 * bm = 2 // new cell BM's routed to TE
242 * ds = 1 // Status reports routed to TE
243 * bfr = 1 // flush buffer
244 */
245 at_send_command("AT+CNMI=1,2,2,1,1", NULL);
246}
247
248static void requestRadioPower(void *data, size_t datalen, RIL_Token t)
249{
250 int onOff;
251
252 int err;
253 ATResponse *p_response = NULL;
254
255 assert (datalen >= sizeof(int *));
256 onOff = ((int *)data)[0];
257
258 if (onOff == 0 && sState != RADIO_STATE_OFF) {
259 err = at_send_command("AT+CFUN=0", &p_response);
260 if (err < 0 || p_response->success == 0) goto error;
261 setRadioState(RADIO_STATE_OFF);
262 } else if (onOff > 0 && sState == RADIO_STATE_OFF) {
263 err = at_send_command("AT+CFUN=1", &p_response);
264 if (err < 0|| p_response->success == 0) {
265 // Some stacks return an error when there is no SIM,
266 // but they really turn the RF portion on
267 // So, if we get an error, let's check to see if it
268 // turned on anyway
269
270 if (isRadioOn() != 1) {
271 goto error;
272 }
273 }
274 setRadioState(RADIO_STATE_SIM_NOT_READY);
275 }
276
277 at_response_free(p_response);
278 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
279 return;
280error:
281 at_response_free(p_response);
282 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
283}
284
Wink Savillef4c4d362009-04-02 01:37:03 -0700285static void requestOrSendDataCallList(RIL_Token *t);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800286
Wink Savillef4c4d362009-04-02 01:37:03 -0700287static void onDataCallListChanged(void *param)
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800288{
Wink Savillef4c4d362009-04-02 01:37:03 -0700289 requestOrSendDataCallList(NULL);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800290}
291
Wink Savillef4c4d362009-04-02 01:37:03 -0700292static void requestDataCallList(void *data, size_t datalen, RIL_Token t)
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800293{
Wink Savillef4c4d362009-04-02 01:37:03 -0700294 requestOrSendDataCallList(&t);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800295}
296
Wink Savillef4c4d362009-04-02 01:37:03 -0700297static void requestOrSendDataCallList(RIL_Token *t)
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800298{
299 ATResponse *p_response;
300 ATLine *p_cur;
301 int err;
302 int n = 0;
303 char *out;
304
305 err = at_send_command_multiline ("AT+CGACT?", "+CGACT:", &p_response);
306 if (err != 0 || p_response->success == 0) {
307 if (t != NULL)
308 RIL_onRequestComplete(*t, RIL_E_GENERIC_FAILURE, NULL, 0);
309 else
Wink Savillef4c4d362009-04-02 01:37:03 -0700310 RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED,
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800311 NULL, 0);
312 return;
313 }
314
315 for (p_cur = p_response->p_intermediates; p_cur != NULL;
316 p_cur = p_cur->p_next)
317 n++;
318
Wink Saville43808972011-01-13 17:39:51 -0800319 RIL_Data_Call_Response_v5 *responses =
320 alloca(n * sizeof(RIL_Data_Call_Response_v5));
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800321
322 int i;
323 for (i = 0; i < n; i++) {
Wink Saville43808972011-01-13 17:39:51 -0800324 responses[i].status = -1;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800325 responses[i].cid = -1;
326 responses[i].active = -1;
327 responses[i].type = "";
Wink Saville43808972011-01-13 17:39:51 -0800328 responses[i].ifname = "";
329 responses[i].addresses = "";
330 responses[i].dnses = "";
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800331 }
332
Wink Saville43808972011-01-13 17:39:51 -0800333 RIL_Data_Call_Response_v5 *response = responses;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800334 for (p_cur = p_response->p_intermediates; p_cur != NULL;
335 p_cur = p_cur->p_next) {
336 char *line = p_cur->line;
337
338 err = at_tok_start(&line);
339 if (err < 0)
340 goto error;
341
342 err = at_tok_nextint(&line, &response->cid);
343 if (err < 0)
344 goto error;
345
346 err = at_tok_nextint(&line, &response->active);
347 if (err < 0)
348 goto error;
349
350 response++;
351 }
352
353 at_response_free(p_response);
354
355 err = at_send_command_multiline ("AT+CGDCONT?", "+CGDCONT:", &p_response);
356 if (err != 0 || p_response->success == 0) {
357 if (t != NULL)
358 RIL_onRequestComplete(*t, RIL_E_GENERIC_FAILURE, NULL, 0);
359 else
Wink Savillef4c4d362009-04-02 01:37:03 -0700360 RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED,
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800361 NULL, 0);
362 return;
363 }
364
365 for (p_cur = p_response->p_intermediates; p_cur != NULL;
366 p_cur = p_cur->p_next) {
367 char *line = p_cur->line;
368 int cid;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800369
370 err = at_tok_start(&line);
371 if (err < 0)
372 goto error;
373
374 err = at_tok_nextint(&line, &cid);
375 if (err < 0)
376 goto error;
377
378 for (i = 0; i < n; i++) {
379 if (responses[i].cid == cid)
380 break;
381 }
382
383 if (i >= n) {
384 /* details for a context we didn't hear about in the last request */
385 continue;
386 }
387
Wink Saville43808972011-01-13 17:39:51 -0800388 // Assume no error
389 responses[i].status = 0;
390
391 // type
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800392 err = at_tok_nextstr(&line, &out);
393 if (err < 0)
394 goto error;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800395 responses[i].type = alloca(strlen(out) + 1);
396 strcpy(responses[i].type, out);
397
Wink Saville43808972011-01-13 17:39:51 -0800398 // APN ignored for v5
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800399 err = at_tok_nextstr(&line, &out);
400 if (err < 0)
401 goto error;
402
Wink Saville43808972011-01-13 17:39:51 -0800403 responses[i].ifname = alloca(strlen(PPP_TTY_PATH) + 1);
404 strcpy(responses[i].ifname, PPP_TTY_PATH);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800405
406 err = at_tok_nextstr(&line, &out);
407 if (err < 0)
408 goto error;
409
Wink Saville43808972011-01-13 17:39:51 -0800410 responses[i].addresses = alloca(strlen(out) + 1);
411 strcpy(responses[i].addresses, out);
412
David 'Digit' Turneraf1298d2011-02-04 13:36:47 +0100413 {
414 char propValue[PROP_VALUE_MAX];
415
416 if (__system_property_get("ro.kernel.qemu", propValue) != 0) {
417 /* We are in the emulator - the dns servers are listed
418 * by the following system properties, setup in
419 * /system/etc/init.goldfish.sh:
420 * - net.eth0.dns1
421 * - net.eth0.dns2
422 * - net.eth0.dns3
423 * - net.eth0.dns4
424 */
425 const int dnslist_sz = 128;
426 char* dnslist = alloca(dnslist_sz);
427 const char* separator = "";
428 int nn;
429
430 dnslist[0] = 0;
431 for (nn = 1; nn <= 4; nn++) {
432 /* Probe net.eth0.dns<n> */
433 char propName[PROP_NAME_MAX];
434 snprintf(propName, sizeof propName, "net.eth0.dns%d", nn);
435
436 /* Ignore if undefined */
437 if (__system_property_get(propName, propValue) == 0) {
438 continue;
439 }
440
441 /* Append the DNS IP address */
442 strlcat(dnslist, separator, dnslist_sz);
443 strlcat(dnslist, propValue, dnslist_sz);
444 separator = " ";
445 }
446 responses[i].dnses = dnslist;
447 }
448 else {
449 /* I don't know where we are, so use the public Google DNS
450 * servers by default.
451 */
452 responses[i].dnses = "8.8.8.8 8.8.4.4";
453 }
454 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800455 }
456
457 at_response_free(p_response);
458
459 if (t != NULL)
460 RIL_onRequestComplete(*t, RIL_E_SUCCESS, responses,
Wink Saville43808972011-01-13 17:39:51 -0800461 n * sizeof(RIL_Data_Call_Response_v5));
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800462 else
Wink Savillef4c4d362009-04-02 01:37:03 -0700463 RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED,
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800464 responses,
Wink Saville43808972011-01-13 17:39:51 -0800465 n * sizeof(RIL_Data_Call_Response_v5));
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800466
467 return;
468
469error:
470 if (t != NULL)
471 RIL_onRequestComplete(*t, RIL_E_GENERIC_FAILURE, NULL, 0);
472 else
Wink Savillef4c4d362009-04-02 01:37:03 -0700473 RIL_onUnsolicitedResponse(RIL_UNSOL_DATA_CALL_LIST_CHANGED,
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800474 NULL, 0);
475
476 at_response_free(p_response);
477}
478
479static void requestQueryNetworkSelectionMode(
480 void *data, size_t datalen, RIL_Token t)
481{
482 int err;
483 ATResponse *p_response = NULL;
484 int response = 0;
485 char *line;
486
487 err = at_send_command_singleline("AT+COPS?", "+COPS:", &p_response);
488
489 if (err < 0 || p_response->success == 0) {
490 goto error;
491 }
492
493 line = p_response->p_intermediates->line;
494
495 err = at_tok_start(&line);
496
497 if (err < 0) {
498 goto error;
499 }
500
501 err = at_tok_nextint(&line, &response);
502
503 if (err < 0) {
504 goto error;
505 }
506
507 RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(int));
508 at_response_free(p_response);
509 return;
510error:
511 at_response_free(p_response);
512 LOGE("requestQueryNetworkSelectionMode must never return error when radio is on");
513 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
514}
515
516static void sendCallStateChanged(void *param)
517{
518 RIL_onUnsolicitedResponse (
519 RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
520 NULL, 0);
521}
522
523static void requestGetCurrentCalls(void *data, size_t datalen, RIL_Token t)
524{
525 int err;
526 ATResponse *p_response;
527 ATLine *p_cur;
528 int countCalls;
529 int countValidCalls;
Wink Saville3d54e742009-05-18 18:00:44 -0700530 RIL_Call *p_calls;
531 RIL_Call **pp_calls;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800532 int i;
533 int needRepoll = 0;
534
535#ifdef WORKAROUND_ERRONEOUS_ANSWER
536 int prevIncomingOrWaitingLine;
537
538 prevIncomingOrWaitingLine = s_incomingOrWaitingLine;
539 s_incomingOrWaitingLine = -1;
540#endif /*WORKAROUND_ERRONEOUS_ANSWER*/
541
542 err = at_send_command_multiline ("AT+CLCC", "+CLCC:", &p_response);
543
544 if (err != 0 || p_response->success == 0) {
545 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
546 return;
547 }
548
549 /* count the calls */
550 for (countCalls = 0, p_cur = p_response->p_intermediates
551 ; p_cur != NULL
552 ; p_cur = p_cur->p_next
553 ) {
554 countCalls++;
555 }
556
557 /* yes, there's an array of pointers and then an array of structures */
558
Wink Saville3d54e742009-05-18 18:00:44 -0700559 pp_calls = (RIL_Call **)alloca(countCalls * sizeof(RIL_Call *));
560 p_calls = (RIL_Call *)alloca(countCalls * sizeof(RIL_Call));
561 memset (p_calls, 0, countCalls * sizeof(RIL_Call));
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800562
563 /* init the pointer array */
564 for(i = 0; i < countCalls ; i++) {
565 pp_calls[i] = &(p_calls[i]);
566 }
567
568 for (countValidCalls = 0, p_cur = p_response->p_intermediates
569 ; p_cur != NULL
570 ; p_cur = p_cur->p_next
571 ) {
572 err = callFromCLCCLine(p_cur->line, p_calls + countValidCalls);
573
574 if (err != 0) {
575 continue;
576 }
577
578#ifdef WORKAROUND_ERRONEOUS_ANSWER
579 if (p_calls[countValidCalls].state == RIL_CALL_INCOMING
580 || p_calls[countValidCalls].state == RIL_CALL_WAITING
581 ) {
582 s_incomingOrWaitingLine = p_calls[countValidCalls].index;
583 }
584#endif /*WORKAROUND_ERRONEOUS_ANSWER*/
585
586 if (p_calls[countValidCalls].state != RIL_CALL_ACTIVE
587 && p_calls[countValidCalls].state != RIL_CALL_HOLDING
588 ) {
589 needRepoll = 1;
590 }
591
592 countValidCalls++;
593 }
594
595#ifdef WORKAROUND_ERRONEOUS_ANSWER
596 // Basically:
597 // A call was incoming or waiting
598 // Now it's marked as active
599 // But we never answered it
600 //
601 // This is probably a bug, and the call will probably
602 // disappear from the call list in the next poll
603 if (prevIncomingOrWaitingLine >= 0
604 && s_incomingOrWaitingLine < 0
605 && s_expectAnswer == 0
606 ) {
607 for (i = 0; i < countValidCalls ; i++) {
608
609 if (p_calls[i].index == prevIncomingOrWaitingLine
610 && p_calls[i].state == RIL_CALL_ACTIVE
611 && s_repollCallsCount < REPOLL_CALLS_COUNT_MAX
612 ) {
613 LOGI(
614 "Hit WORKAROUND_ERRONOUS_ANSWER case."
615 " Repoll count: %d\n", s_repollCallsCount);
616 s_repollCallsCount++;
617 goto error;
618 }
619 }
620 }
621
622 s_expectAnswer = 0;
623 s_repollCallsCount = 0;
624#endif /*WORKAROUND_ERRONEOUS_ANSWER*/
625
Wink Saville3d54e742009-05-18 18:00:44 -0700626 RIL_onRequestComplete(t, RIL_E_SUCCESS, pp_calls,
627 countValidCalls * sizeof (RIL_Call *));
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800628
629 at_response_free(p_response);
630
631#ifdef POLL_CALL_STATE
632 if (countValidCalls) { // We don't seem to get a "NO CARRIER" message from
633 // smd, so we're forced to poll until the call ends.
634#else
635 if (needRepoll) {
636#endif
637 RIL_requestTimedCallback (sendCallStateChanged, NULL, &TIMEVAL_CALLSTATEPOLL);
638 }
639
640 return;
641error:
642 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
643 at_response_free(p_response);
644}
645
646static void requestDial(void *data, size_t datalen, RIL_Token t)
647{
648 RIL_Dial *p_dial;
649 char *cmd;
650 const char *clir;
651 int ret;
652
653 p_dial = (RIL_Dial *)data;
654
655 switch (p_dial->clir) {
656 case 1: clir = "I"; break; /*invocation*/
657 case 2: clir = "i"; break; /*suppression*/
658 default:
659 case 0: clir = ""; break; /*subscription default*/
660 }
661
662 asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);
663
664 ret = at_send_command(cmd, NULL);
665
666 free(cmd);
667
668 /* success or failure is ignored by the upper layer here.
669 it will call GET_CURRENT_CALLS and determine success that way */
670 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
671}
672
673static void requestWriteSmsToSim(void *data, size_t datalen, RIL_Token t)
674{
675 RIL_SMS_WriteArgs *p_args;
676 char *cmd;
677 int length;
678 int err;
679 ATResponse *p_response = NULL;
680
681 p_args = (RIL_SMS_WriteArgs *)data;
682
683 length = strlen(p_args->pdu)/2;
684 asprintf(&cmd, "AT+CMGW=%d,%d", length, p_args->status);
685
686 err = at_send_command_sms(cmd, p_args->pdu, "+CMGW:", &p_response);
687
688 if (err != 0 || p_response->success == 0) goto error;
689
690 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
691 at_response_free(p_response);
692
693 return;
694error:
695 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
696 at_response_free(p_response);
697}
698
699static void requestHangup(void *data, size_t datalen, RIL_Token t)
700{
701 int *p_line;
702
703 int ret;
704 char *cmd;
705
706 p_line = (int *)data;
707
708 // 3GPP 22.030 6.5.5
709 // "Releases a specific active call X"
710 asprintf(&cmd, "AT+CHLD=1%d", p_line[0]);
711
712 ret = at_send_command(cmd, NULL);
713
714 free(cmd);
715
716 /* success or failure is ignored by the upper layer here.
717 it will call GET_CURRENT_CALLS and determine success that way */
718 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
719}
720
721static void requestSignalStrength(void *data, size_t datalen, RIL_Token t)
722{
723 ATResponse *p_response = NULL;
724 int err;
725 int response[2];
726 char *line;
727
728 err = at_send_command_singleline("AT+CSQ", "+CSQ:", &p_response);
729
730 if (err < 0 || p_response->success == 0) {
731 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
732 goto error;
733 }
734
735 line = p_response->p_intermediates->line;
736
737 err = at_tok_start(&line);
738 if (err < 0) goto error;
739
740 err = at_tok_nextint(&line, &(response[0]));
741 if (err < 0) goto error;
742
743 err = at_tok_nextint(&line, &(response[1]));
744 if (err < 0) goto error;
745
746 RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response));
747
748 at_response_free(p_response);
749 return;
750
751error:
752 LOGE("requestSignalStrength must never return an error when radio is on");
753 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
754 at_response_free(p_response);
755}
756
757static void requestRegistrationState(int request, void *data,
758 size_t datalen, RIL_Token t)
759{
760 int err;
761 int response[4];
762 char * responseStr[4];
763 ATResponse *p_response = NULL;
764 const char *cmd;
765 const char *prefix;
766 char *line, *p;
767 int commas;
768 int skip;
769 int count = 3;
770
771
772 if (request == RIL_REQUEST_REGISTRATION_STATE) {
773 cmd = "AT+CREG?";
774 prefix = "+CREG:";
775 } else if (request == RIL_REQUEST_GPRS_REGISTRATION_STATE) {
776 cmd = "AT+CGREG?";
777 prefix = "+CGREG:";
778 } else {
779 assert(0);
780 goto error;
781 }
782
783 err = at_send_command_singleline(cmd, prefix, &p_response);
784
785 if (err != 0) goto error;
786
787 line = p_response->p_intermediates->line;
788
789 err = at_tok_start(&line);
790 if (err < 0) goto error;
791
792 /* Ok you have to be careful here
793 * The solicited version of the CREG response is
794 * +CREG: n, stat, [lac, cid]
795 * and the unsolicited version is
796 * +CREG: stat, [lac, cid]
797 * The <n> parameter is basically "is unsolicited creg on?"
798 * which it should always be
799 *
800 * Now we should normally get the solicited version here,
801 * but the unsolicited version could have snuck in
802 * so we have to handle both
803 *
804 * Also since the LAC and CID are only reported when registered,
805 * we can have 1, 2, 3, or 4 arguments here
806 *
807 * finally, a +CGREG: answer may have a fifth value that corresponds
808 * to the network type, as in;
809 *
810 * +CGREG: n, stat [,lac, cid [,networkType]]
811 */
812
813 /* count number of commas */
814 commas = 0;
815 for (p = line ; *p != '\0' ;p++) {
816 if (*p == ',') commas++;
817 }
818
819 switch (commas) {
820 case 0: /* +CREG: <stat> */
821 err = at_tok_nextint(&line, &response[0]);
822 if (err < 0) goto error;
823 response[1] = -1;
824 response[2] = -1;
825 break;
826
827 case 1: /* +CREG: <n>, <stat> */
828 err = at_tok_nextint(&line, &skip);
829 if (err < 0) goto error;
830 err = at_tok_nextint(&line, &response[0]);
831 if (err < 0) goto error;
832 response[1] = -1;
833 response[2] = -1;
834 if (err < 0) goto error;
835 break;
836
837 case 2: /* +CREG: <stat>, <lac>, <cid> */
838 err = at_tok_nextint(&line, &response[0]);
839 if (err < 0) goto error;
840 err = at_tok_nexthexint(&line, &response[1]);
841 if (err < 0) goto error;
842 err = at_tok_nexthexint(&line, &response[2]);
843 if (err < 0) goto error;
844 break;
845 case 3: /* +CREG: <n>, <stat>, <lac>, <cid> */
846 err = at_tok_nextint(&line, &skip);
847 if (err < 0) goto error;
848 err = at_tok_nextint(&line, &response[0]);
849 if (err < 0) goto error;
850 err = at_tok_nexthexint(&line, &response[1]);
851 if (err < 0) goto error;
852 err = at_tok_nexthexint(&line, &response[2]);
853 if (err < 0) goto error;
854 break;
855 /* special case for CGREG, there is a fourth parameter
856 * that is the network type (unknown/gprs/edge/umts)
857 */
858 case 4: /* +CGREG: <n>, <stat>, <lac>, <cid>, <networkType> */
859 err = at_tok_nextint(&line, &skip);
860 if (err < 0) goto error;
861 err = at_tok_nextint(&line, &response[0]);
862 if (err < 0) goto error;
863 err = at_tok_nexthexint(&line, &response[1]);
864 if (err < 0) goto error;
865 err = at_tok_nexthexint(&line, &response[2]);
866 if (err < 0) goto error;
867 err = at_tok_nexthexint(&line, &response[3]);
868 if (err < 0) goto error;
869 count = 4;
870 break;
871 default:
872 goto error;
873 }
874
875 asprintf(&responseStr[0], "%d", response[0]);
John Wang6f189a62009-06-30 13:12:53 -0700876 asprintf(&responseStr[1], "%x", response[1]);
877 asprintf(&responseStr[2], "%x", response[2]);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800878
879 if (count > 3)
880 asprintf(&responseStr[3], "%d", response[3]);
881
882 RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, count*sizeof(char*));
883 at_response_free(p_response);
884
885 return;
886error:
887 LOGE("requestRegistrationState must never return an error when radio is on");
888 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
889 at_response_free(p_response);
890}
891
892static void requestOperator(void *data, size_t datalen, RIL_Token t)
893{
894 int err;
895 int i;
896 int skip;
897 ATLine *p_cur;
898 char *response[3];
899
900 memset(response, 0, sizeof(response));
901
902 ATResponse *p_response = NULL;
903
904 err = at_send_command_multiline(
905 "AT+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?",
906 "+COPS:", &p_response);
907
908 /* we expect 3 lines here:
909 * +COPS: 0,0,"T - Mobile"
910 * +COPS: 0,1,"TMO"
911 * +COPS: 0,2,"310170"
912 */
913
914 if (err != 0) goto error;
915
916 for (i = 0, p_cur = p_response->p_intermediates
917 ; p_cur != NULL
918 ; p_cur = p_cur->p_next, i++
919 ) {
920 char *line = p_cur->line;
921
922 err = at_tok_start(&line);
923 if (err < 0) goto error;
924
925 err = at_tok_nextint(&line, &skip);
926 if (err < 0) goto error;
927
928 // If we're unregistered, we may just get
929 // a "+COPS: 0" response
930 if (!at_tok_hasmore(&line)) {
931 response[i] = NULL;
932 continue;
933 }
934
935 err = at_tok_nextint(&line, &skip);
936 if (err < 0) goto error;
937
938 // a "+COPS: 0, n" response is also possible
939 if (!at_tok_hasmore(&line)) {
940 response[i] = NULL;
941 continue;
942 }
943
944 err = at_tok_nextstr(&line, &(response[i]));
945 if (err < 0) goto error;
946 }
947
948 if (i != 3) {
949 /* expect 3 lines exactly */
950 goto error;
951 }
952
953 RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response));
954 at_response_free(p_response);
955
956 return;
957error:
958 LOGE("requestOperator must not return error when radio is on");
959 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
960 at_response_free(p_response);
961}
962
963static void requestSendSMS(void *data, size_t datalen, RIL_Token t)
964{
965 int err;
966 const char *smsc;
967 const char *pdu;
968 int tpLayerLength;
969 char *cmd1, *cmd2;
970 RIL_SMS_Response response;
971 ATResponse *p_response = NULL;
972
973 smsc = ((const char **)data)[0];
974 pdu = ((const char **)data)[1];
975
976 tpLayerLength = strlen(pdu)/2;
977
978 // "NULL for default SMSC"
979 if (smsc == NULL) {
980 smsc= "00";
981 }
982
983 asprintf(&cmd1, "AT+CMGS=%d", tpLayerLength);
984 asprintf(&cmd2, "%s%s", smsc, pdu);
985
986 err = at_send_command_sms(cmd1, cmd2, "+CMGS:", &p_response);
987
988 if (err != 0 || p_response->success == 0) goto error;
989
990 memset(&response, 0, sizeof(response));
991
992 /* FIXME fill in messageRef and ackPDU */
993
994 RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
995 at_response_free(p_response);
996
997 return;
998error:
999 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1000 at_response_free(p_response);
1001}
1002
Wink Savillef4c4d362009-04-02 01:37:03 -07001003static void requestSetupDataCall(void *data, size_t datalen, RIL_Token t)
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001004{
1005 const char *apn;
1006 char *cmd;
1007 int err;
1008 ATResponse *p_response = NULL;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001009
Wink Savillef4c4d362009-04-02 01:37:03 -07001010 apn = ((const char **)data)[2];
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001011
1012#ifdef USE_TI_COMMANDS
1013 // Config for multislot class 10 (probably default anyway eh?)
1014 err = at_send_command("AT%CPRIM=\"GMM\",\"CONFIG MULTISLOT_CLASS=<10>\"",
1015 NULL);
1016
1017 err = at_send_command("AT%DATA=2,\"UART\",1,,\"SER\",\"UART\",0", NULL);
1018#endif /* USE_TI_COMMANDS */
1019
1020 int fd, qmistatus;
1021 size_t cur = 0;
1022 size_t len;
1023 ssize_t written, rlen;
1024 char status[32] = {0};
1025 int retry = 10;
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001026 const char *pdp_type;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001027
1028 LOGD("requesting data connection to APN '%s'", apn);
1029
1030 fd = open ("/dev/qmi", O_RDWR);
1031 if (fd >= 0) { /* the device doesn't exist on the emulator */
1032
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001033 LOGD("opened the qmi device\n");
1034 asprintf(&cmd, "up:%s", apn);
1035 len = strlen(cmd);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001036
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001037 while (cur < len) {
1038 do {
1039 written = write (fd, cmd + cur, len - cur);
1040 } while (written < 0 && errno == EINTR);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001041
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001042 if (written < 0) {
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001043 LOGE("### ERROR writing to /dev/qmi");
1044 close(fd);
1045 goto error;
1046 }
1047
1048 cur += written;
1049 }
1050
1051 // wait for interface to come online
1052
1053 do {
1054 sleep(1);
1055 do {
1056 rlen = read(fd, status, 31);
1057 } while (rlen < 0 && errno == EINTR);
1058
1059 if (rlen < 0) {
1060 LOGE("### ERROR reading from /dev/qmi");
1061 close(fd);
1062 goto error;
1063 } else {
1064 status[rlen] = '\0';
1065 LOGD("### status: %s", status);
1066 }
1067 } while (strncmp(status, "STATE=up", 8) && strcmp(status, "online") && --retry);
1068
1069 close(fd);
1070
1071 if (retry == 0) {
1072 LOGE("### Failed to get data connection up\n");
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001073 goto error;
1074 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001075
1076 qmistatus = system("netcfg rmnet0 dhcp");
1077
1078 LOGD("netcfg rmnet0 dhcp: status %d\n", qmistatus);
1079
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001080 if (qmistatus < 0) goto error;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001081
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001082 } else {
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001083
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001084 if (datalen > 6 * sizeof(char *)) {
1085 pdp_type = ((const char **)data)[6];
1086 } else {
1087 pdp_type = "IP";
1088 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001089
David 'Digit' Turneraf1298d2011-02-04 13:36:47 +01001090 asprintf(&cmd, "AT+CGDCONT=1,\"%s\",\"%s\",,0,0", pdp_type, apn);
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001091 //FIXME check for error here
1092 err = at_send_command(cmd, NULL);
1093 free(cmd);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001094
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001095 // Set required QoS params to default
1096 err = at_send_command("AT+CGQREQ=1", NULL);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001097
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001098 // Set minimum QoS params to default
1099 err = at_send_command("AT+CGQMIN=1", NULL);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001100
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001101 // packet-domain event reporting
1102 err = at_send_command("AT+CGEREP=1,0", NULL);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001103
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001104 // Hangup anything that's happening there now
1105 err = at_send_command("AT+CGACT=1,0", NULL);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001106
Lorenzo Colitti4f81dcf2010-09-01 19:38:57 -07001107 // Start data on PDP context 1
1108 err = at_send_command("ATD*99***1#", &p_response);
1109
1110 if (err < 0 || p_response->success == 0) {
1111 goto error;
1112 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001113 }
1114
Wink Saville43808972011-01-13 17:39:51 -08001115 requestOrSendDataCallList(&t);
1116
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001117 at_response_free(p_response);
1118
1119 return;
1120error:
1121 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1122 at_response_free(p_response);
1123
1124}
1125
1126static void requestSMSAcknowledge(void *data, size_t datalen, RIL_Token t)
1127{
1128 int ackSuccess;
1129 int err;
1130
1131 ackSuccess = ((int *)data)[0];
1132
1133 if (ackSuccess == 1) {
1134 err = at_send_command("AT+CNMA=1", NULL);
1135 } else if (ackSuccess == 0) {
1136 err = at_send_command("AT+CNMA=2", NULL);
1137 } else {
1138 LOGE("unsupported arg to RIL_REQUEST_SMS_ACKNOWLEDGE\n");
1139 goto error;
1140 }
1141
1142 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1143error:
1144 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1145
1146}
1147
1148static void requestSIM_IO(void *data, size_t datalen, RIL_Token t)
1149{
1150 ATResponse *p_response = NULL;
1151 RIL_SIM_IO_Response sr;
1152 int err;
1153 char *cmd = NULL;
1154 RIL_SIM_IO *p_args;
1155 char *line;
1156
1157 memset(&sr, 0, sizeof(sr));
1158
1159 p_args = (RIL_SIM_IO *)data;
1160
1161 /* FIXME handle pin2 */
1162
1163 if (p_args->data == NULL) {
1164 asprintf(&cmd, "AT+CRSM=%d,%d,%d,%d,%d",
1165 p_args->command, p_args->fileid,
1166 p_args->p1, p_args->p2, p_args->p3);
1167 } else {
1168 asprintf(&cmd, "AT+CRSM=%d,%d,%d,%d,%d,%s",
1169 p_args->command, p_args->fileid,
1170 p_args->p1, p_args->p2, p_args->p3, p_args->data);
1171 }
1172
1173 err = at_send_command_singleline(cmd, "+CRSM:", &p_response);
1174
1175 if (err < 0 || p_response->success == 0) {
1176 goto error;
1177 }
1178
1179 line = p_response->p_intermediates->line;
1180
1181 err = at_tok_start(&line);
1182 if (err < 0) goto error;
1183
1184 err = at_tok_nextint(&line, &(sr.sw1));
1185 if (err < 0) goto error;
1186
1187 err = at_tok_nextint(&line, &(sr.sw2));
1188 if (err < 0) goto error;
1189
1190 if (at_tok_hasmore(&line)) {
1191 err = at_tok_nextstr(&line, &(sr.simResponse));
1192 if (err < 0) goto error;
1193 }
1194
1195 RIL_onRequestComplete(t, RIL_E_SUCCESS, &sr, sizeof(sr));
1196 at_response_free(p_response);
1197 free(cmd);
1198
1199 return;
1200error:
1201 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1202 at_response_free(p_response);
1203 free(cmd);
1204
1205}
1206
1207static void requestEnterSimPin(void* data, size_t datalen, RIL_Token t)
1208{
1209 ATResponse *p_response = NULL;
1210 int err;
1211 char* cmd = NULL;
1212 const char** strings = (const char**)data;;
1213
1214 if ( datalen == sizeof(char*) ) {
1215 asprintf(&cmd, "AT+CPIN=%s", strings[0]);
1216 } else if ( datalen == 2*sizeof(char*) ) {
1217 asprintf(&cmd, "AT+CPIN=%s,%s", strings[0], strings[1]);
1218 } else
1219 goto error;
1220
1221 err = at_send_command_singleline(cmd, "+CPIN:", &p_response);
1222 free(cmd);
1223
1224 if (err < 0 || p_response->success == 0) {
1225error:
1226 RIL_onRequestComplete(t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
1227 } else {
1228 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1229 }
1230 at_response_free(p_response);
1231}
1232
1233
1234static void requestSendUSSD(void *data, size_t datalen, RIL_Token t)
1235{
1236 const char *ussdRequest;
1237
1238 ussdRequest = (char *)(data);
1239
1240
1241 RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
1242
1243// @@@ TODO
1244
1245}
1246
1247
1248/*** Callback methods from the RIL library to us ***/
1249
1250/**
1251 * Call from RIL to us to make a RIL_REQUEST
1252 *
1253 * Must be completed with a call to RIL_onRequestComplete()
1254 *
1255 * RIL_onRequestComplete() may be called from any thread, before or after
1256 * this function returns.
1257 *
1258 * Will always be called from the same thread, so returning here implies
1259 * that the radio is ready to process another command (whether or not
1260 * the previous command has completed).
1261 */
1262static void
1263onRequest (int request, void *data, size_t datalen, RIL_Token t)
1264{
1265 ATResponse *p_response;
1266 int err;
1267
1268 LOGD("onRequest: %s", requestToString(request));
1269
1270 /* Ignore all requests except RIL_REQUEST_GET_SIM_STATUS
1271 * when RADIO_STATE_UNAVAILABLE.
1272 */
1273 if (sState == RADIO_STATE_UNAVAILABLE
1274 && request != RIL_REQUEST_GET_SIM_STATUS
1275 ) {
1276 RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
1277 return;
1278 }
1279
1280 /* Ignore all non-power requests when RADIO_STATE_OFF
1281 * (except RIL_REQUEST_GET_SIM_STATUS)
1282 */
1283 if (sState == RADIO_STATE_OFF
1284 && !(request == RIL_REQUEST_RADIO_POWER
1285 || request == RIL_REQUEST_GET_SIM_STATUS)
1286 ) {
1287 RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
1288 return;
1289 }
1290
1291 switch (request) {
1292 case RIL_REQUEST_GET_SIM_STATUS: {
Wink Savillef6aa7c12009-04-30 14:20:52 -07001293 RIL_CardStatus *p_card_status;
1294 char *p_buffer;
1295 int buffer_size;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001296
Wink Savillef6aa7c12009-04-30 14:20:52 -07001297 int result = getCardStatus(&p_card_status);
1298 if (result == RIL_E_SUCCESS) {
1299 p_buffer = (char *)p_card_status;
1300 buffer_size = sizeof(*p_card_status);
1301 } else {
1302 p_buffer = NULL;
1303 buffer_size = 0;
1304 }
1305 RIL_onRequestComplete(t, result, p_buffer, buffer_size);
1306 freeCardStatus(p_card_status);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001307 break;
1308 }
1309 case RIL_REQUEST_GET_CURRENT_CALLS:
1310 requestGetCurrentCalls(data, datalen, t);
1311 break;
1312 case RIL_REQUEST_DIAL:
1313 requestDial(data, datalen, t);
1314 break;
1315 case RIL_REQUEST_HANGUP:
1316 requestHangup(data, datalen, t);
1317 break;
1318 case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
1319 // 3GPP 22.030 6.5.5
1320 // "Releases all held calls or sets User Determined User Busy
1321 // (UDUB) for a waiting call."
1322 at_send_command("AT+CHLD=0", NULL);
1323
1324 /* success or failure is ignored by the upper layer here.
1325 it will call GET_CURRENT_CALLS and determine success that way */
1326 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1327 break;
1328 case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
1329 // 3GPP 22.030 6.5.5
1330 // "Releases all active calls (if any exist) and accepts
1331 // the other (held or waiting) call."
1332 at_send_command("AT+CHLD=1", NULL);
1333
1334 /* success or failure is ignored by the upper layer here.
1335 it will call GET_CURRENT_CALLS and determine success that way */
1336 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1337 break;
1338 case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
1339 // 3GPP 22.030 6.5.5
1340 // "Places all active calls (if any exist) on hold and accepts
1341 // the other (held or waiting) call."
1342 at_send_command("AT+CHLD=2", NULL);
1343
1344#ifdef WORKAROUND_ERRONEOUS_ANSWER
1345 s_expectAnswer = 1;
1346#endif /* WORKAROUND_ERRONEOUS_ANSWER */
1347
1348 /* success or failure is ignored by the upper layer here.
1349 it will call GET_CURRENT_CALLS and determine success that way */
1350 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1351 break;
1352 case RIL_REQUEST_ANSWER:
1353 at_send_command("ATA", NULL);
1354
1355#ifdef WORKAROUND_ERRONEOUS_ANSWER
1356 s_expectAnswer = 1;
1357#endif /* WORKAROUND_ERRONEOUS_ANSWER */
1358
1359 /* success or failure is ignored by the upper layer here.
1360 it will call GET_CURRENT_CALLS and determine success that way */
1361 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1362 break;
1363 case RIL_REQUEST_CONFERENCE:
1364 // 3GPP 22.030 6.5.5
1365 // "Adds a held call to the conversation"
1366 at_send_command("AT+CHLD=3", NULL);
1367
1368 /* success or failure is ignored by the upper layer here.
1369 it will call GET_CURRENT_CALLS and determine success that way */
1370 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1371 break;
1372 case RIL_REQUEST_UDUB:
1373 /* user determined user busy */
1374 /* sometimes used: ATH */
1375 at_send_command("ATH", NULL);
1376
1377 /* success or failure is ignored by the upper layer here.
1378 it will call GET_CURRENT_CALLS and determine success that way */
1379 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1380 break;
1381
1382 case RIL_REQUEST_SEPARATE_CONNECTION:
1383 {
1384 char cmd[12];
1385 int party = ((int*)data)[0];
1386
1387 // Make sure that party is in a valid range.
1388 // (Note: The Telephony middle layer imposes a range of 1 to 7.
1389 // It's sufficient for us to just make sure it's single digit.)
1390 if (party > 0 && party < 10) {
1391 sprintf(cmd, "AT+CHLD=2%d", party);
1392 at_send_command(cmd, NULL);
1393 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1394 } else {
1395 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1396 }
1397 }
1398 break;
1399
1400 case RIL_REQUEST_SIGNAL_STRENGTH:
1401 requestSignalStrength(data, datalen, t);
1402 break;
1403 case RIL_REQUEST_REGISTRATION_STATE:
1404 case RIL_REQUEST_GPRS_REGISTRATION_STATE:
1405 requestRegistrationState(request, data, datalen, t);
1406 break;
1407 case RIL_REQUEST_OPERATOR:
1408 requestOperator(data, datalen, t);
1409 break;
1410 case RIL_REQUEST_RADIO_POWER:
1411 requestRadioPower(data, datalen, t);
1412 break;
1413 case RIL_REQUEST_DTMF: {
1414 char c = ((char *)data)[0];
1415 char *cmd;
1416 asprintf(&cmd, "AT+VTS=%c", (int)c);
1417 at_send_command(cmd, NULL);
1418 free(cmd);
1419 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1420 break;
1421 }
1422 case RIL_REQUEST_SEND_SMS:
1423 requestSendSMS(data, datalen, t);
1424 break;
Wink Savillef4c4d362009-04-02 01:37:03 -07001425 case RIL_REQUEST_SETUP_DATA_CALL:
1426 requestSetupDataCall(data, datalen, t);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001427 break;
1428 case RIL_REQUEST_SMS_ACKNOWLEDGE:
1429 requestSMSAcknowledge(data, datalen, t);
1430 break;
1431
1432 case RIL_REQUEST_GET_IMSI:
1433 p_response = NULL;
1434 err = at_send_command_numeric("AT+CIMI", &p_response);
1435
1436 if (err < 0 || p_response->success == 0) {
1437 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1438 } else {
1439 RIL_onRequestComplete(t, RIL_E_SUCCESS,
1440 p_response->p_intermediates->line, sizeof(char *));
1441 }
1442 at_response_free(p_response);
1443 break;
1444
1445 case RIL_REQUEST_GET_IMEI:
1446 p_response = NULL;
1447 err = at_send_command_numeric("AT+CGSN", &p_response);
1448
1449 if (err < 0 || p_response->success == 0) {
1450 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1451 } else {
1452 RIL_onRequestComplete(t, RIL_E_SUCCESS,
1453 p_response->p_intermediates->line, sizeof(char *));
1454 }
1455 at_response_free(p_response);
1456 break;
1457
1458 case RIL_REQUEST_SIM_IO:
1459 requestSIM_IO(data,datalen,t);
1460 break;
1461
1462 case RIL_REQUEST_SEND_USSD:
1463 requestSendUSSD(data, datalen, t);
1464 break;
1465
1466 case RIL_REQUEST_CANCEL_USSD:
1467 p_response = NULL;
1468 err = at_send_command_numeric("AT+CUSD=2", &p_response);
1469
1470 if (err < 0 || p_response->success == 0) {
1471 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1472 } else {
1473 RIL_onRequestComplete(t, RIL_E_SUCCESS,
1474 p_response->p_intermediates->line, sizeof(char *));
1475 }
1476 at_response_free(p_response);
1477 break;
1478
1479 case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC:
1480 at_send_command("AT+COPS=0", NULL);
1481 break;
1482
Wink Savillef4c4d362009-04-02 01:37:03 -07001483 case RIL_REQUEST_DATA_CALL_LIST:
1484 requestDataCallList(data, datalen, t);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001485 break;
1486
1487 case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
1488 requestQueryNetworkSelectionMode(data, datalen, t);
1489 break;
1490
1491 case RIL_REQUEST_OEM_HOOK_RAW:
1492 // echo back data
1493 RIL_onRequestComplete(t, RIL_E_SUCCESS, data, datalen);
1494 break;
1495
1496
1497 case RIL_REQUEST_OEM_HOOK_STRINGS: {
1498 int i;
1499 const char ** cur;
1500
1501 LOGD("got OEM_HOOK_STRINGS: 0x%8p %lu", data, (long)datalen);
1502
1503
1504 for (i = (datalen / sizeof (char *)), cur = (const char **)data ;
1505 i > 0 ; cur++, i --) {
1506 LOGD("> '%s'", *cur);
1507 }
1508
1509 // echo back strings
1510 RIL_onRequestComplete(t, RIL_E_SUCCESS, data, datalen);
1511 break;
1512 }
1513
1514 case RIL_REQUEST_WRITE_SMS_TO_SIM:
1515 requestWriteSmsToSim(data, datalen, t);
1516 break;
1517
1518 case RIL_REQUEST_DELETE_SMS_ON_SIM: {
1519 char * cmd;
1520 p_response = NULL;
1521 asprintf(&cmd, "AT+CMGD=%d", ((int *)data)[0]);
1522 err = at_send_command(cmd, &p_response);
1523 free(cmd);
1524 if (err < 0 || p_response->success == 0) {
1525 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
1526 } else {
1527 RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
1528 }
1529 at_response_free(p_response);
1530 break;
1531 }
1532
1533 case RIL_REQUEST_ENTER_SIM_PIN:
1534 case RIL_REQUEST_ENTER_SIM_PUK:
1535 case RIL_REQUEST_ENTER_SIM_PIN2:
1536 case RIL_REQUEST_ENTER_SIM_PUK2:
1537 case RIL_REQUEST_CHANGE_SIM_PIN:
1538 case RIL_REQUEST_CHANGE_SIM_PIN2:
1539 requestEnterSimPin(data, datalen, t);
1540 break;
1541
1542 default:
1543 RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
1544 break;
1545 }
1546}
1547
1548/**
1549 * Synchronous call from the RIL to us to return current radio state.
1550 * RADIO_STATE_UNAVAILABLE should be the initial state.
1551 */
1552static RIL_RadioState
1553currentState()
1554{
1555 return sState;
1556}
1557/**
1558 * Call from RIL to us to find out whether a specific request code
1559 * is supported by this implementation.
1560 *
1561 * Return 1 for "supported" and 0 for "unsupported"
1562 */
1563
1564static int
1565onSupports (int requestCode)
1566{
1567 //@@@ todo
1568
1569 return 1;
1570}
1571
1572static void onCancel (RIL_Token t)
1573{
1574 //@@@todo
1575
1576}
1577
1578static const char * getVersion(void)
1579{
1580 return "android reference-ril 1.0";
1581}
1582
1583static void
1584setRadioState(RIL_RadioState newState)
1585{
1586 RIL_RadioState oldState;
1587
1588 pthread_mutex_lock(&s_state_mutex);
1589
1590 oldState = sState;
1591
1592 if (s_closed > 0) {
1593 // If we're closed, the only reasonable state is
1594 // RADIO_STATE_UNAVAILABLE
1595 // This is here because things on the main thread
1596 // may attempt to change the radio state after the closed
1597 // event happened in another thread
1598 newState = RADIO_STATE_UNAVAILABLE;
1599 }
1600
1601 if (sState != newState || s_closed > 0) {
1602 sState = newState;
1603
1604 pthread_cond_broadcast (&s_state_cond);
1605 }
1606
1607 pthread_mutex_unlock(&s_state_mutex);
1608
1609
1610 /* do these outside of the mutex */
1611 if (sState != oldState) {
1612 RIL_onUnsolicitedResponse (RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED,
1613 NULL, 0);
1614
1615 /* FIXME onSimReady() and onRadioPowerOn() cannot be called
1616 * from the AT reader thread
1617 * Currently, this doesn't happen, but if that changes then these
1618 * will need to be dispatched on the request thread
1619 */
1620 if (sState == RADIO_STATE_SIM_READY) {
1621 onSIMReady();
1622 } else if (sState == RADIO_STATE_SIM_NOT_READY) {
1623 onRadioPowerOn();
1624 }
1625 }
1626}
1627
John Wang309ac292009-07-30 14:53:23 -07001628/** Returns SIM_NOT_READY on error */
David 'Digit' Turneraf1298d2011-02-04 13:36:47 +01001629static SIM_Status
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001630getSIMStatus()
1631{
1632 ATResponse *p_response = NULL;
1633 int err;
1634 int ret;
1635 char *cpinLine;
1636 char *cpinResult;
1637
1638 if (sState == RADIO_STATE_OFF || sState == RADIO_STATE_UNAVAILABLE) {
John Wang309ac292009-07-30 14:53:23 -07001639 ret = SIM_NOT_READY;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001640 goto done;
1641 }
1642
1643 err = at_send_command_singleline("AT+CPIN?", "+CPIN:", &p_response);
1644
1645 if (err != 0) {
John Wang309ac292009-07-30 14:53:23 -07001646 ret = SIM_NOT_READY;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001647 goto done;
1648 }
1649
1650 switch (at_get_cme_error(p_response)) {
1651 case CME_SUCCESS:
1652 break;
1653
1654 case CME_SIM_NOT_INSERTED:
John Wang309ac292009-07-30 14:53:23 -07001655 ret = SIM_ABSENT;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001656 goto done;
1657
1658 default:
John Wang309ac292009-07-30 14:53:23 -07001659 ret = SIM_NOT_READY;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001660 goto done;
1661 }
1662
1663 /* CPIN? has succeeded, now look at the result */
1664
1665 cpinLine = p_response->p_intermediates->line;
1666 err = at_tok_start (&cpinLine);
1667
1668 if (err < 0) {
John Wang309ac292009-07-30 14:53:23 -07001669 ret = SIM_NOT_READY;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001670 goto done;
1671 }
1672
1673 err = at_tok_nextstr(&cpinLine, &cpinResult);
1674
1675 if (err < 0) {
John Wang309ac292009-07-30 14:53:23 -07001676 ret = SIM_NOT_READY;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001677 goto done;
1678 }
1679
1680 if (0 == strcmp (cpinResult, "SIM PIN")) {
John Wang309ac292009-07-30 14:53:23 -07001681 ret = SIM_PIN;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001682 goto done;
1683 } else if (0 == strcmp (cpinResult, "SIM PUK")) {
John Wang309ac292009-07-30 14:53:23 -07001684 ret = SIM_PUK;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001685 goto done;
1686 } else if (0 == strcmp (cpinResult, "PH-NET PIN")) {
John Wang309ac292009-07-30 14:53:23 -07001687 return SIM_NETWORK_PERSONALIZATION;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001688 } else if (0 != strcmp (cpinResult, "READY")) {
1689 /* we're treating unsupported lock types as "sim absent" */
John Wang309ac292009-07-30 14:53:23 -07001690 ret = SIM_ABSENT;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001691 goto done;
1692 }
1693
1694 at_response_free(p_response);
1695 p_response = NULL;
1696 cpinResult = NULL;
1697
John Wang309ac292009-07-30 14:53:23 -07001698 ret = SIM_READY;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001699
1700done:
1701 at_response_free(p_response);
1702 return ret;
1703}
1704
1705
1706/**
Wink Savillef6aa7c12009-04-30 14:20:52 -07001707 * Get the current card status.
1708 *
1709 * This must be freed using freeCardStatus.
1710 * @return: On success returns RIL_E_SUCCESS
1711 */
1712static int getCardStatus(RIL_CardStatus **pp_card_status) {
1713 static RIL_AppStatus app_status_array[] = {
John Wang309ac292009-07-30 14:53:23 -07001714 // SIM_ABSENT = 0
Wink Savillef6aa7c12009-04-30 14:20:52 -07001715 { RIL_APPTYPE_UNKNOWN, RIL_APPSTATE_UNKNOWN, RIL_PERSOSUBSTATE_UNKNOWN,
1716 NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
John Wang309ac292009-07-30 14:53:23 -07001717 // SIM_NOT_READY = 1
Wink Savillef6aa7c12009-04-30 14:20:52 -07001718 { RIL_APPTYPE_SIM, RIL_APPSTATE_DETECTED, RIL_PERSOSUBSTATE_UNKNOWN,
1719 NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
John Wang309ac292009-07-30 14:53:23 -07001720 // SIM_READY = 2
Wink Savillef6aa7c12009-04-30 14:20:52 -07001721 { RIL_APPTYPE_SIM, RIL_APPSTATE_READY, RIL_PERSOSUBSTATE_READY,
1722 NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
John Wang309ac292009-07-30 14:53:23 -07001723 // SIM_PIN = 3
Wink Savillef6aa7c12009-04-30 14:20:52 -07001724 { RIL_APPTYPE_SIM, RIL_APPSTATE_PIN, RIL_PERSOSUBSTATE_UNKNOWN,
1725 NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
John Wang309ac292009-07-30 14:53:23 -07001726 // SIM_PUK = 4
Wink Savillef6aa7c12009-04-30 14:20:52 -07001727 { RIL_APPTYPE_SIM, RIL_APPSTATE_PUK, RIL_PERSOSUBSTATE_UNKNOWN,
1728 NULL, NULL, 0, RIL_PINSTATE_ENABLED_BLOCKED, RIL_PINSTATE_UNKNOWN },
John Wang309ac292009-07-30 14:53:23 -07001729 // SIM_NETWORK_PERSONALIZATION = 5
Wink Savillef6aa7c12009-04-30 14:20:52 -07001730 { RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO, RIL_PERSOSUBSTATE_SIM_NETWORK,
1731 NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN }
1732 };
1733 RIL_CardState card_state;
1734 int num_apps;
1735
1736 int sim_status = getSIMStatus();
John Wang309ac292009-07-30 14:53:23 -07001737 if (sim_status == SIM_ABSENT) {
Wink Savillef6aa7c12009-04-30 14:20:52 -07001738 card_state = RIL_CARDSTATE_ABSENT;
1739 num_apps = 0;
1740 } else {
1741 card_state = RIL_CARDSTATE_PRESENT;
1742 num_apps = 1;
1743 }
1744
1745 // Allocate and initialize base card status.
1746 RIL_CardStatus *p_card_status = malloc(sizeof(RIL_CardStatus));
1747 p_card_status->card_state = card_state;
1748 p_card_status->universal_pin_state = RIL_PINSTATE_UNKNOWN;
1749 p_card_status->gsm_umts_subscription_app_index = RIL_CARD_MAX_APPS;
1750 p_card_status->cdma_subscription_app_index = RIL_CARD_MAX_APPS;
1751 p_card_status->num_applications = num_apps;
1752
1753 // Initialize application status
1754 int i;
1755 for (i = 0; i < RIL_CARD_MAX_APPS; i++) {
John Wang309ac292009-07-30 14:53:23 -07001756 p_card_status->applications[i] = app_status_array[SIM_ABSENT];
Wink Savillef6aa7c12009-04-30 14:20:52 -07001757 }
1758
1759 // Pickup the appropriate application status
1760 // that reflects sim_status for gsm.
1761 if (num_apps != 0) {
1762 // Only support one app, gsm
1763 p_card_status->num_applications = 1;
1764 p_card_status->gsm_umts_subscription_app_index = 0;
1765
1766 // Get the correct app status
1767 p_card_status->applications[0] = app_status_array[sim_status];
1768 }
1769
1770 *pp_card_status = p_card_status;
1771 return RIL_E_SUCCESS;
1772}
1773
1774/**
1775 * Free the card status returned by getCardStatus
1776 */
1777static void freeCardStatus(RIL_CardStatus *p_card_status) {
1778 free(p_card_status);
1779}
1780
1781/**
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001782 * SIM ready means any commands that access the SIM will work, including:
1783 * AT+CPIN, AT+CSMS, AT+CNMI, AT+CRSM
1784 * (all SMS-related commands)
1785 */
1786
1787static void pollSIMState (void *param)
1788{
1789 ATResponse *p_response;
1790 int ret;
1791
1792 if (sState != RADIO_STATE_SIM_NOT_READY) {
1793 // no longer valid to poll
1794 return;
1795 }
1796
1797 switch(getSIMStatus()) {
John Wang309ac292009-07-30 14:53:23 -07001798 case SIM_ABSENT:
1799 case SIM_PIN:
1800 case SIM_PUK:
1801 case SIM_NETWORK_PERSONALIZATION:
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001802 default:
1803 setRadioState(RADIO_STATE_SIM_LOCKED_OR_ABSENT);
1804 return;
1805
John Wang309ac292009-07-30 14:53:23 -07001806 case SIM_NOT_READY:
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001807 RIL_requestTimedCallback (pollSIMState, NULL, &TIMEVAL_SIMPOLL);
1808 return;
1809
John Wang309ac292009-07-30 14:53:23 -07001810 case SIM_READY:
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001811 setRadioState(RADIO_STATE_SIM_READY);
1812 return;
1813 }
1814}
1815
1816/** returns 1 if on, 0 if off, and -1 on error */
1817static int isRadioOn()
1818{
1819 ATResponse *p_response = NULL;
1820 int err;
1821 char *line;
1822 char ret;
1823
1824 err = at_send_command_singleline("AT+CFUN?", "+CFUN:", &p_response);
1825
1826 if (err < 0 || p_response->success == 0) {
1827 // assume radio is off
1828 goto error;
1829 }
1830
1831 line = p_response->p_intermediates->line;
1832
1833 err = at_tok_start(&line);
1834 if (err < 0) goto error;
1835
1836 err = at_tok_nextbool(&line, &ret);
1837 if (err < 0) goto error;
1838
1839 at_response_free(p_response);
1840
1841 return (int)ret;
1842
1843error:
1844
1845 at_response_free(p_response);
1846 return -1;
1847}
1848
1849/**
1850 * Initialize everything that can be configured while we're still in
1851 * AT+CFUN=0
1852 */
1853static void initializeCallback(void *param)
1854{
1855 ATResponse *p_response = NULL;
1856 int err;
1857
1858 setRadioState (RADIO_STATE_OFF);
1859
1860 at_handshake();
1861
1862 /* note: we don't check errors here. Everything important will
1863 be handled in onATTimeout and onATReaderClosed */
1864
1865 /* atchannel is tolerant of echo but it must */
1866 /* have verbose result codes */
1867 at_send_command("ATE0Q0V1", NULL);
1868
1869 /* No auto-answer */
1870 at_send_command("ATS0=0", NULL);
1871
1872 /* Extended errors */
1873 at_send_command("AT+CMEE=1", NULL);
1874
1875 /* Network registration events */
1876 err = at_send_command("AT+CREG=2", &p_response);
1877
1878 /* some handsets -- in tethered mode -- don't support CREG=2 */
1879 if (err < 0 || p_response->success == 0) {
1880 at_send_command("AT+CREG=1", NULL);
1881 }
1882
1883 at_response_free(p_response);
1884
1885 /* GPRS registration events */
1886 at_send_command("AT+CGREG=1", NULL);
1887
1888 /* Call Waiting notifications */
1889 at_send_command("AT+CCWA=1", NULL);
1890
1891 /* Alternating voice/data off */
1892 at_send_command("AT+CMOD=0", NULL);
1893
1894 /* Not muted */
1895 at_send_command("AT+CMUT=0", NULL);
1896
1897 /* +CSSU unsolicited supp service notifications */
1898 at_send_command("AT+CSSN=0,1", NULL);
1899
1900 /* no connected line identification */
1901 at_send_command("AT+COLP=0", NULL);
1902
1903 /* HEX character set */
1904 at_send_command("AT+CSCS=\"HEX\"", NULL);
1905
1906 /* USSD unsolicited */
1907 at_send_command("AT+CUSD=1", NULL);
1908
1909 /* Enable +CGEV GPRS event notifications, but don't buffer */
1910 at_send_command("AT+CGEREP=1,0", NULL);
1911
1912 /* SMS PDU mode */
1913 at_send_command("AT+CMGF=0", NULL);
1914
1915#ifdef USE_TI_COMMANDS
1916
1917 at_send_command("AT%CPI=3", NULL);
1918
1919 /* TI specific -- notifications when SMS is ready (currently ignored) */
1920 at_send_command("AT%CSTAT=1", NULL);
1921
1922#endif /* USE_TI_COMMANDS */
1923
1924
1925 /* assume radio is off on error */
1926 if (isRadioOn() > 0) {
1927 setRadioState (RADIO_STATE_SIM_NOT_READY);
1928 }
1929}
1930
1931static void waitForClose()
1932{
1933 pthread_mutex_lock(&s_state_mutex);
1934
1935 while (s_closed == 0) {
1936 pthread_cond_wait(&s_state_cond, &s_state_mutex);
1937 }
1938
1939 pthread_mutex_unlock(&s_state_mutex);
1940}
1941
1942/**
1943 * Called by atchannel when an unsolicited line appears
1944 * This is called on atchannel's reader thread. AT commands may
1945 * not be issued here
1946 */
1947static void onUnsolicited (const char *s, const char *sms_pdu)
1948{
1949 char *line = NULL;
1950 int err;
1951
1952 /* Ignore unsolicited responses until we're initialized.
1953 * This is OK because the RIL library will poll for initial state
1954 */
1955 if (sState == RADIO_STATE_UNAVAILABLE) {
1956 return;
1957 }
1958
1959 if (strStartsWith(s, "%CTZV:")) {
1960 /* TI specific -- NITZ time */
1961 char *response;
1962
1963 line = strdup(s);
1964 at_tok_start(&line);
1965
1966 err = at_tok_nextstr(&line, &response);
1967
1968 if (err != 0) {
1969 LOGE("invalid NITZ line %s\n", s);
1970 } else {
1971 RIL_onUnsolicitedResponse (
1972 RIL_UNSOL_NITZ_TIME_RECEIVED,
1973 response, strlen(response));
1974 }
1975 } else if (strStartsWith(s,"+CRING:")
1976 || strStartsWith(s,"RING")
1977 || strStartsWith(s,"NO CARRIER")
1978 || strStartsWith(s,"+CCWA")
1979 ) {
1980 RIL_onUnsolicitedResponse (
1981 RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
1982 NULL, 0);
1983#ifdef WORKAROUND_FAKE_CGEV
Wink Savillef4c4d362009-04-02 01:37:03 -07001984 RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL); //TODO use new function
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001985#endif /* WORKAROUND_FAKE_CGEV */
1986 } else if (strStartsWith(s,"+CREG:")
1987 || strStartsWith(s,"+CGREG:")
1988 ) {
1989 RIL_onUnsolicitedResponse (
1990 RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED,
1991 NULL, 0);
1992#ifdef WORKAROUND_FAKE_CGEV
Wink Saville7f856802009-06-09 10:23:37 -07001993 RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001994#endif /* WORKAROUND_FAKE_CGEV */
1995 } else if (strStartsWith(s, "+CMT:")) {
1996 RIL_onUnsolicitedResponse (
1997 RIL_UNSOL_RESPONSE_NEW_SMS,
1998 sms_pdu, strlen(sms_pdu));
1999 } else if (strStartsWith(s, "+CDS:")) {
2000 RIL_onUnsolicitedResponse (
2001 RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT,
2002 sms_pdu, strlen(sms_pdu));
2003 } else if (strStartsWith(s, "+CGEV:")) {
2004 /* Really, we can ignore NW CLASS and ME CLASS events here,
2005 * but right now we don't since extranous
Wink Savillef4c4d362009-04-02 01:37:03 -07002006 * RIL_UNSOL_DATA_CALL_LIST_CHANGED calls are tolerated
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08002007 */
2008 /* can't issue AT commands here -- call on main thread */
Wink Savillef4c4d362009-04-02 01:37:03 -07002009 RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08002010#ifdef WORKAROUND_FAKE_CGEV
2011 } else if (strStartsWith(s, "+CME ERROR: 150")) {
Wink Savillef4c4d362009-04-02 01:37:03 -07002012 RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08002013#endif /* WORKAROUND_FAKE_CGEV */
2014 }
2015}
2016
2017/* Called on command or reader thread */
2018static void onATReaderClosed()
2019{
2020 LOGI("AT channel closed\n");
2021 at_close();
2022 s_closed = 1;
2023
2024 setRadioState (RADIO_STATE_UNAVAILABLE);
2025}
2026
2027/* Called on command thread */
2028static void onATTimeout()
2029{
2030 LOGI("AT channel timeout; closing\n");
2031 at_close();
2032
2033 s_closed = 1;
2034
2035 /* FIXME cause a radio reset here */
2036
2037 setRadioState (RADIO_STATE_UNAVAILABLE);
2038}
2039
2040static void usage(char *s)
2041{
2042#ifdef RIL_SHLIB
2043 fprintf(stderr, "reference-ril requires: -p <tcp port> or -d /dev/tty_device\n");
2044#else
2045 fprintf(stderr, "usage: %s [-p <tcp port>] [-d /dev/tty_device]\n", s);
2046 exit(-1);
2047#endif
2048}
2049
2050static void *
2051mainLoop(void *param)
2052{
2053 int fd;
2054 int ret;
2055
2056 AT_DUMP("== ", "entering mainLoop()", -1 );
2057 at_set_on_reader_closed(onATReaderClosed);
2058 at_set_on_timeout(onATTimeout);
2059
2060 for (;;) {
2061 fd = -1;
2062 while (fd < 0) {
2063 if (s_port > 0) {
2064 fd = socket_loopback_client(s_port, SOCK_STREAM);
2065 } else if (s_device_socket) {
The Android Open Source Projecte6e6fb22009-03-18 17:39:47 -07002066 if (!strcmp(s_device_path, "/dev/socket/qemud")) {
2067 /* Qemu-specific control socket */
2068 fd = socket_local_client( "qemud",
2069 ANDROID_SOCKET_NAMESPACE_RESERVED,
2070 SOCK_STREAM );
2071 if (fd >= 0 ) {
2072 char answer[2];
2073
2074 if ( write(fd, "gsm", 3) != 3 ||
2075 read(fd, answer, 2) != 2 ||
2076 memcmp(answer, "OK", 2) != 0)
2077 {
2078 close(fd);
2079 fd = -1;
2080 }
2081 }
2082 }
2083 else
2084 fd = socket_local_client( s_device_path,
2085 ANDROID_SOCKET_NAMESPACE_FILESYSTEM,
2086 SOCK_STREAM );
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08002087 } else if (s_device_path != NULL) {
2088 fd = open (s_device_path, O_RDWR);
2089 if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) {
2090 /* disable echo on serial ports */
2091 struct termios ios;
2092 tcgetattr( fd, &ios );
2093 ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
2094 tcsetattr( fd, TCSANOW, &ios );
2095 }
2096 }
2097
2098 if (fd < 0) {
2099 perror ("opening AT interface. retrying...");
2100 sleep(10);
2101 /* never returns */
2102 }
2103 }
2104
2105 s_closed = 0;
2106 ret = at_open(fd, onUnsolicited);
2107
2108 if (ret < 0) {
2109 LOGE ("AT error %d on at_open\n", ret);
2110 return 0;
2111 }
2112
2113 RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
2114
2115 // Give initializeCallback a chance to dispatched, since
2116 // we don't presently have a cancellation mechanism
2117 sleep(1);
2118
2119 waitForClose();
2120 LOGI("Re-opening after close");
2121 }
2122}
2123
2124#ifdef RIL_SHLIB
2125
2126pthread_t s_tid_mainloop;
2127
2128const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
2129{
2130 int ret;
2131 int fd = -1;
2132 int opt;
2133 pthread_attr_t attr;
2134
2135 s_rilenv = env;
2136
2137 while ( -1 != (opt = getopt(argc, argv, "p:d:s:"))) {
2138 switch (opt) {
2139 case 'p':
2140 s_port = atoi(optarg);
2141 if (s_port == 0) {
2142 usage(argv[0]);
2143 return NULL;
2144 }
2145 LOGI("Opening loopback port %d\n", s_port);
2146 break;
2147
2148 case 'd':
2149 s_device_path = optarg;
2150 LOGI("Opening tty device %s\n", s_device_path);
2151 break;
2152
2153 case 's':
2154 s_device_path = optarg;
2155 s_device_socket = 1;
2156 LOGI("Opening socket %s\n", s_device_path);
2157 break;
2158
2159 default:
2160 usage(argv[0]);
2161 return NULL;
2162 }
2163 }
2164
2165 if (s_port < 0 && s_device_path == NULL) {
2166 usage(argv[0]);
2167 return NULL;
2168 }
2169
2170 pthread_attr_init (&attr);
2171 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2172 ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);
2173
2174 return &s_callbacks;
2175}
2176#else /* RIL_SHLIB */
2177int main (int argc, char **argv)
2178{
2179 int ret;
2180 int fd = -1;
2181 int opt;
2182
2183 while ( -1 != (opt = getopt(argc, argv, "p:d:"))) {
2184 switch (opt) {
2185 case 'p':
2186 s_port = atoi(optarg);
2187 if (s_port == 0) {
2188 usage(argv[0]);
2189 }
2190 LOGI("Opening loopback port %d\n", s_port);
2191 break;
2192
2193 case 'd':
2194 s_device_path = optarg;
2195 LOGI("Opening tty device %s\n", s_device_path);
2196 break;
2197
2198 case 's':
2199 s_device_path = optarg;
2200 s_device_socket = 1;
2201 LOGI("Opening socket %s\n", s_device_path);
2202 break;
2203
2204 default:
2205 usage(argv[0]);
2206 }
2207 }
2208
2209 if (s_port < 0 && s_device_path == NULL) {
2210 usage(argv[0]);
2211 }
2212
2213 RIL_register(&s_callbacks);
2214
2215 mainLoop(NULL);
2216
2217 return 0;
2218}
2219
2220#endif /* RIL_SHLIB */