hardware/ril
Revision | 59b1395df5af65ff9128dd5e28bd2673eed05d80 (tree) |
---|---|
Zeit | 2014-07-11 15:48:05 |
Autor | Chih-Wei Huang <cwhuang@linu...> |
Commiter | Chih-Wei Huang |
update to huawei-ril 1.0.0.8
@@ -4,12 +4,15 @@ LOCAL_PATH := $(call my-dir) | ||
4 | 4 | include $(CLEAR_VARS) |
5 | 5 | |
6 | 6 | LOCAL_SRC_FILES := \ |
7 | + agc.c \ | |
7 | 8 | at_tok.c \ |
8 | 9 | atchannel.c \ |
10 | + echocancel.c \ | |
9 | 11 | fcp_parser.c \ |
10 | 12 | gsm.c \ |
11 | 13 | huaweigeneric-ril.c \ |
12 | 14 | misc.c \ |
15 | + net-utils.c \ | |
13 | 16 | requestdatahandler.c \ |
14 | 17 | sms.c \ |
15 | 18 | sms_gsm.c \ |
@@ -34,3 +34,52 @@ The modules has still a few problems: | ||
34 | 34 | ---- |
35 | 35 | |
36 | 36 | pppd must be suid ROOT, and kernel must include ppp support compiled in |
37 | + /system/etc/ppp/ip-up and also /system/etc/ppp/ip-down must also be suid ROOT | |
38 | + | |
39 | +---- | |
40 | + | |
41 | + You also need the following files in /system/etc/ppp | |
42 | + | |
43 | +---- | |
44 | +/system/etc/ppp/ip-up: | |
45 | + | |
46 | +#!/system/bin/sh | |
47 | +case $1 in | |
48 | + ppp1) | |
49 | + /android/bin/iptables --flush; | |
50 | + /android/bin/iptables --table nat --flush; | |
51 | + /android/bin/iptables --delete-chain; | |
52 | + /android/bin/iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE; | |
53 | + /android/bin/iptables --append FORWARD --in-interface ppp1 -j ACCEPT; | |
54 | + echo 0 > /proc/sys/net/ipv4/ip_forward; | |
55 | + echo 1 > /proc/sys/net/ipv4/ip_forward; | |
56 | + ;; | |
57 | + ppp0) | |
58 | + /system/bin/setprop "net.interfaces.defaultroute" "$LINKNAME"; | |
59 | + ;; | |
60 | +esac | |
61 | + | |
62 | +/system/bin/setprop "net.$LINKNAME.dns1" "$DNS1" | |
63 | +/system/bin/setprop "net.$LINKNAME.dns2" "$DNS2" | |
64 | +/system/bin/setprop "net.$LINKNAME.local-ip" "$IPLOCAL" | |
65 | +/system/bin/setprop "net.$LINKNAME.remote-ip" "$IPREMOTE" | |
66 | +/system/bin/setprop "net.$LINKNAME.gw" "$IPREMOTE" | |
67 | +/system/bin/setprop "net.$LINKNAME.if" "$IFNAME" | |
68 | + | |
69 | + | |
70 | +---- | |
71 | +/system/etc/ppp/ip-down: | |
72 | + | |
73 | +#!/system/bin/sh | |
74 | +case $1 in | |
75 | + ppp1) | |
76 | + echo 0 > /proc/sys/net/ipv4/ip_forward; | |
77 | + ;; | |
78 | +esac | |
79 | + | |
80 | +/system/bin/setprop "net.$LINKNAME.dns1" "" | |
81 | +/system/bin/setprop "net.$LINKNAME.dns2" "" | |
82 | +/system/bin/setprop "net.$LINKNAME.local-ip" "" | |
83 | +/system/bin/setprop "net.$LINKNAME.remote-ip" "" | |
84 | +/system/bin/setprop "net.$LINKNAME.gw" "" | |
85 | +/system/bin/setprop "net.$LINKNAME.if" "" |
@@ -0,0 +1,130 @@ | ||
1 | +/* | |
2 | +* This AGC algorithm was taken from isdn2h323 (http://www.telos.de). It was | |
3 | +* converted from C++ to C, and modified to add control over the recording level. | |
4 | +* Converted to fixed point by Phil Frisbie, Jr. 4/12/2003 | |
5 | +*/ | |
6 | + | |
7 | +#include "agc.h" | |
8 | + | |
9 | +#define LOG_NDEBUG 0 | |
10 | +#define LOG_TAG "RILAudioAGC" | |
11 | +#include <utils/Log.h> | |
12 | + | |
13 | +void agc_init(struct agc_ctx* ctx, short level /* 16384-32767*/ ) | |
14 | +{ | |
15 | + ALOGD("[%p] Initializing AGC: level: %d",ctx, level); | |
16 | + ctx->sample_max = 1; | |
17 | + ctx->counter = 0; | |
18 | + ctx->igain = 65536; | |
19 | + if (level < 16384) { | |
20 | + level = 16384; | |
21 | + } | |
22 | + ctx->ipeak = (int)(level * 65536); | |
23 | + ctx->silence_counter = 0; | |
24 | +} | |
25 | + | |
26 | +void agc_process_16bit(struct agc_ctx *ctx, short *buffer, int len) | |
27 | +{ | |
28 | + int i; | |
29 | + for (i=0; i<len; i++) { | |
30 | + | |
31 | + long gain_new; | |
32 | + int sample; | |
33 | + | |
34 | + /* get the abs of buffer[i] */ | |
35 | + sample = buffer[i]; | |
36 | + sample = (sample < 0 ? -(sample) : sample); | |
37 | + | |
38 | + if(sample > (int)ctx->sample_max) { | |
39 | + /* update the max */ | |
40 | + ctx->sample_max = (unsigned int)sample; | |
41 | + } | |
42 | + ctx->counter ++; | |
43 | + | |
44 | + /* Will we get an overflow with the current gain factor? */ | |
45 | + if (((sample * ctx->igain) >> 16) > ctx->ipeak) { | |
46 | + /* Yes: Calculate new gain. */ | |
47 | + ctx->igain = ((ctx->ipeak / ctx->sample_max) * 62259) >> 16; | |
48 | + ctx->silence_counter = 0; | |
49 | + buffer[i] = (short) ((buffer[i] * ctx->igain) >> 16); | |
50 | + continue; | |
51 | + } | |
52 | + | |
53 | + /* Calculate new gain factor 10x per second */ | |
54 | + if (ctx->counter >= 8000/10) { | |
55 | + if (ctx->sample_max > 800) { /* speaking? */ | |
56 | + gain_new = ((ctx->ipeak / ctx->sample_max) * 62259) >> 16; | |
57 | + | |
58 | + if (ctx->silence_counter > 40) /* pause -> speaking */ | |
59 | + ctx->igain += (gain_new - ctx->igain) >> 2; | |
60 | + else | |
61 | + ctx->igain += (gain_new - ctx->igain) / 20; | |
62 | + | |
63 | + ctx->silence_counter = 0; | |
64 | + } else { /* silence */ | |
65 | + ctx->silence_counter++; | |
66 | + /* silence > 2 seconds: reduce gain */ | |
67 | + if ((ctx->igain > 65536) && (ctx->silence_counter >= 20)) | |
68 | + ctx->igain = (ctx->igain * 62259) >> 16; | |
69 | + } | |
70 | + ctx->counter = 0; | |
71 | + ctx->sample_max = 1; | |
72 | + } | |
73 | + buffer[i] = (short) ((buffer[i] * ctx->igain) >> 16); | |
74 | + } | |
75 | + | |
76 | + //ALOGD("[%p] Gain: %d",ctx, ctx->igain); | |
77 | +} | |
78 | + | |
79 | +void agc_process_8bit(struct agc_ctx *ctx, unsigned char *buffer, int len) | |
80 | +{ | |
81 | + int i; | |
82 | + for (i=0; i<len; i++) { | |
83 | + | |
84 | + long gain_new; | |
85 | + int sample; | |
86 | + | |
87 | + /* get the abs of buffer[i] */ | |
88 | + sample = (buffer[i] - 128) << 8; | |
89 | + sample = (sample < 0 ? -(sample) : sample); | |
90 | + | |
91 | + if(sample > (int)ctx->sample_max) { | |
92 | + /* update the max */ | |
93 | + ctx->sample_max = (unsigned int)sample; | |
94 | + } | |
95 | + ctx->counter ++; | |
96 | + | |
97 | + /* Will we get an overflow with the current gain factor? */ | |
98 | + if (((sample * ctx->igain) >> 16) > ctx->ipeak) { | |
99 | + /* Yes: Calculate new gain. */ | |
100 | + ctx->igain = ((ctx->ipeak / ctx->sample_max) * 62259) >> 16; | |
101 | + ctx->silence_counter = 0; | |
102 | + buffer[i] = (unsigned char) (( ((buffer[i] - 128) << 8) * ctx->igain) >> 24) + 128; | |
103 | + continue; | |
104 | + } | |
105 | + | |
106 | + /* Calculate new gain factor 10x per second */ | |
107 | + if (ctx->counter >= 8000/10) { | |
108 | + if (ctx->sample_max > 800) { /* speaking? */ | |
109 | + gain_new = ((ctx->ipeak / ctx->sample_max) * 62259) >> 16; | |
110 | + | |
111 | + if (ctx->silence_counter > 40) /* pause -> speaking */ | |
112 | + ctx->igain += (gain_new - ctx->igain) >> 2; | |
113 | + else | |
114 | + ctx->igain += (gain_new - ctx->igain) / 20; | |
115 | + | |
116 | + ctx->silence_counter = 0; | |
117 | + } else { /* silence */ | |
118 | + ctx->silence_counter++; | |
119 | + /* silence > 2 seconds: reduce gain */ | |
120 | + if ((ctx->igain > 65536) && (ctx->silence_counter >= 20)) | |
121 | + ctx->igain = (ctx->igain * 62259) >> 16; | |
122 | + } | |
123 | + ctx->counter = 0; | |
124 | + ctx->sample_max = 1; | |
125 | + } | |
126 | + buffer[i] = (unsigned char) (( ((buffer[i] - 128) << 8) * ctx->igain) >> 24) + 128; | |
127 | + } | |
128 | + | |
129 | + //ALOGD("[%p] Gain: %d",ctx, ctx->igain); | |
130 | +} |
@@ -0,0 +1,43 @@ | ||
1 | +/* | |
2 | + ** | |
3 | + ** Copyright 2006, The Android Open Source Project | |
4 | + ** Copyright 2012 Eduardo Jos[e Tagle <ejtagle@tutopia.com> | |
5 | + ** | |
6 | + ** Licensed under the Apache License, Version 2.0 (the "License"); | |
7 | + ** you may not use this file except in compliance with the License. | |
8 | + ** You may obtain a copy of the License at | |
9 | + ** | |
10 | + ** http://www.apache.org/licenses/LICENSE-2.0 | |
11 | + ** | |
12 | + ** Unless required by applicable law or agreed to in writing, software | |
13 | + ** distributed under the License is distributed on an "AS IS" BASIS, | |
14 | + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 | + ** See the License for the specific language governing permissions and | |
16 | + ** limitations under the License. | |
17 | + */ | |
18 | + | |
19 | +#ifndef _AGC_H | |
20 | +#define _AGC_H | |
21 | + | |
22 | +struct agc_ctx { | |
23 | + unsigned int sample_max; | |
24 | + int counter; | |
25 | + int igain; | |
26 | + int ipeak; | |
27 | + int silence_counter; | |
28 | +}; | |
29 | + | |
30 | +#ifdef __cplusplus | |
31 | +extern "C" { | |
32 | +#endif | |
33 | + | |
34 | +void agc_init(struct agc_ctx* ctx, short level /* 16384-32767*/ ); | |
35 | +void agc_process_16bit(struct agc_ctx *ctx, short *buffer, int samples); | |
36 | +void agc_process_8bit(struct agc_ctx *ctx, unsigned char *buffer, int samples); | |
37 | + | |
38 | + | |
39 | +#ifdef __cplusplus | |
40 | +} | |
41 | +#endif | |
42 | + | |
43 | +#endif |
@@ -27,16 +27,18 @@ | ||
27 | 27 | */ |
28 | 28 | int at_tok_start(char **p_cur) |
29 | 29 | { |
30 | - if (*p_cur == NULL) | |
30 | + if (*p_cur == NULL) { | |
31 | 31 | return -1; |
32 | + } | |
32 | 33 | |
33 | 34 | /* Skip prefix, |
34 | 35 | consume "^[^:]:". */ |
35 | 36 | |
36 | 37 | *p_cur = strchr(*p_cur, ':'); |
37 | 38 | |
38 | - if (*p_cur == NULL) | |
39 | + if (*p_cur == NULL) { | |
39 | 40 | return -1; |
41 | + } | |
40 | 42 | |
41 | 43 | (*p_cur)++; |
42 | 44 |
@@ -45,33 +47,34 @@ int at_tok_start(char **p_cur) | ||
45 | 47 | |
46 | 48 | static void skipWhiteSpace(char **p_cur) |
47 | 49 | { |
48 | - if (*p_cur == NULL) | |
49 | - return; | |
50 | + if (*p_cur == NULL) return; | |
50 | 51 | |
51 | - while (**p_cur != '\0' && isspace(**p_cur)) | |
52 | + while (**p_cur != '\0' && isspace(**p_cur)) { | |
52 | 53 | (*p_cur)++; |
53 | 54 | } |
55 | +} | |
54 | 56 | |
55 | 57 | static void skipNextComma(char **p_cur) |
56 | 58 | { |
57 | - if (*p_cur == NULL) | |
58 | - return; | |
59 | + if (*p_cur == NULL) return; | |
59 | 60 | |
60 | - while (**p_cur != '\0' && **p_cur != ',') | |
61 | + while (**p_cur != '\0' && **p_cur != ',') { | |
61 | 62 | (*p_cur)++; |
63 | + } | |
62 | 64 | |
63 | - if (**p_cur == ',') | |
65 | + if (**p_cur == ',') { | |
64 | 66 | (*p_cur)++; |
65 | 67 | } |
68 | +} | |
66 | 69 | |
67 | 70 | /** |
68 | 71 | * If the first none space character is a quotation mark, returns the string |
69 | 72 | * between two quotation marks, else returns the content before the first comma. |
70 | 73 | * Updates *p_cur. |
71 | 74 | */ |
72 | -static char * nextTok(char **p_cur) | |
75 | +static char *nextTok(char **p_cur) | |
73 | 76 | { |
74 | - char *ret = NULL; | |
77 | + char *ret; | |
75 | 78 | |
76 | 79 | skipWhiteSpace(p_cur); |
77 | 80 |
@@ -85,47 +88,48 @@ static char * nextTok(char **p_cur) | ||
85 | 88 | |
86 | 89 | while (state != END) { |
87 | 90 | switch (state) { |
88 | - case NORMAL: | |
89 | - switch (**p_cur) { | |
90 | - case '\\': | |
91 | - state = ESCAPE; | |
91 | + case NORMAL: | |
92 | + switch (**p_cur) { | |
93 | + case '\\': | |
94 | + state = ESCAPE; | |
95 | + break; | |
96 | + case '"': | |
97 | + state = END; | |
98 | + break; | |
99 | + case '\0': | |
100 | + /* | |
101 | + * Error case, parsing string is not quoted by ending | |
102 | + * double quote, e.g. "bla bla, this function expects input | |
103 | + * string to be NULL terminated, so that the loop can exit. | |
104 | + */ | |
105 | + ret = NULL; | |
106 | + goto exit; | |
107 | + default: | |
108 | + /* Stays in normal case. */ | |
109 | + break; | |
110 | + } | |
92 | 111 | break; |
93 | - case '"': | |
94 | - state = END; | |
112 | + | |
113 | + case ESCAPE: | |
114 | + state = NORMAL; | |
95 | 115 | break; |
96 | - case '\0': | |
97 | - /* | |
98 | - * Error case, parsing string is not quoted by ending | |
99 | - * double quote, e.g. "bla bla, this function expects input | |
100 | - * string to be NULL terminated, so that the loop can exit. | |
101 | - */ | |
102 | - ret = NULL; | |
103 | - goto exit; | |
116 | + | |
104 | 117 | default: |
105 | - /* Stays in normal case. */ | |
118 | + /* This should never happen. */ | |
106 | 119 | break; |
107 | - } | |
108 | - break; | |
109 | - | |
110 | - case ESCAPE: | |
111 | - state = NORMAL; | |
112 | - break; | |
113 | - | |
114 | - default: | |
115 | - /* This should never happen. */ | |
116 | - break; | |
117 | 120 | } |
118 | 121 | |
119 | 122 | if (state == END) { |
120 | 123 | **p_cur = '\0'; |
121 | 124 | } |
122 | 125 | |
123 | - (*p_cur)++; | |
126 | + (*p_cur)++; | |
124 | 127 | } |
125 | 128 | skipNextComma(p_cur); |
126 | 129 | } else { |
127 | 130 | ret = strsep(p_cur, ","); |
128 | 131 | } |
132 | + | |
129 | 133 | exit: |
130 | 134 | return ret; |
131 | 135 | } |
@@ -148,9 +152,9 @@ static int at_tok_nextint_base(char **p_cur, int *p_out, int base, int uns) | ||
148 | 152 | |
149 | 153 | ret = nextTok(p_cur); |
150 | 154 | |
151 | - if (ret == NULL) | |
155 | + if (ret == NULL) { | |
152 | 156 | return -1; |
153 | - else { | |
157 | + } else { | |
154 | 158 | long l; |
155 | 159 | char *end; |
156 | 160 |
@@ -161,8 +165,9 @@ static int at_tok_nextint_base(char **p_cur, int *p_out, int base, int uns) | ||
161 | 165 | |
162 | 166 | *p_out = (int)l; |
163 | 167 | |
164 | - if (end == ret) | |
168 | + if (end == ret) { | |
165 | 169 | return -1; |
170 | + } | |
166 | 171 | } |
167 | 172 | |
168 | 173 | return 0; |
@@ -197,29 +202,34 @@ int at_tok_nextbool(char **p_cur, char *p_out) | ||
197 | 202 | |
198 | 203 | ret = at_tok_nextint(p_cur, &result); |
199 | 204 | |
200 | - if (ret < 0) | |
205 | + if (ret < 0) { | |
201 | 206 | return -1; |
207 | + } | |
202 | 208 | |
203 | 209 | /* Booleans should be 0 or 1. */ |
204 | - if (!(result == 0 || result == 1)) | |
210 | + if (!(result == 0 || result == 1)) { | |
205 | 211 | return -1; |
212 | + } | |
206 | 213 | |
207 | - if (p_out != NULL) | |
214 | + if (p_out != NULL) { | |
208 | 215 | *p_out = (char)result; |
209 | - else | |
216 | + } else { | |
210 | 217 | return -1; |
218 | + } | |
211 | 219 | |
212 | 220 | return ret; |
213 | 221 | } |
214 | 222 | |
215 | 223 | int at_tok_nextstr(char **p_cur, char **p_out) |
216 | 224 | { |
217 | - if (*p_cur == NULL) | |
225 | + if (*p_cur == NULL) { | |
218 | 226 | return -1; |
227 | + } | |
219 | 228 | |
220 | 229 | *p_out = nextTok(p_cur); |
221 | - if (*p_out == NULL) | |
230 | + if (*p_out == NULL) { | |
222 | 231 | return -1; |
232 | + } | |
223 | 233 | |
224 | 234 | return 0; |
225 | 235 | } |
@@ -236,8 +246,9 @@ int at_tok_charcounter(char *p_in, char needle, int *p_out) | ||
236 | 246 | char *p_cur = p_in; |
237 | 247 | int num_found = 0; |
238 | 248 | |
239 | - if (p_in == NULL) | |
249 | + if (p_in == NULL) { | |
240 | 250 | return -1; |
251 | + } | |
241 | 252 | |
242 | 253 | while (*p_cur != '\0') { |
243 | 254 | if (*p_cur == needle) { |
@@ -138,14 +138,12 @@ static int initializeAtContext(void) | ||
138 | 138 | ac = (struct atcontext *)pthread_getspecific(key); |
139 | 139 | |
140 | 140 | if (ac == NULL) { |
141 | - ac = (struct atcontext *) malloc(sizeof(struct atcontext)); | |
141 | + ac = (struct atcontext *) calloc(1, sizeof(struct atcontext)); | |
142 | 142 | if (ac == NULL) { |
143 | 143 | ALOGE("%s() Failed to allocate memory", __func__); |
144 | 144 | goto error; |
145 | 145 | } |
146 | 146 | |
147 | - memset(ac, 0, sizeof(struct atcontext)); | |
148 | - | |
149 | 147 | ac->fd = -1; |
150 | 148 | ac->readerCmdFds[0] = -1; |
151 | 149 | ac->readerCmdFds[1] = -1; |
@@ -223,8 +221,8 @@ void AT_DUMP(const char* prefix, const char* buff, int len) | ||
223 | 221 | |
224 | 222 | #ifndef HAVE_ANDROID_OS |
225 | 223 | int pthread_cond_timeout_np(pthread_cond_t *cond, |
226 | - pthread_mutex_t * mutex, | |
227 | - unsigned msecs) | |
224 | + pthread_mutex_t * mutex, | |
225 | + unsigned msecs) | |
228 | 226 | { |
229 | 227 | struct timespec ts; |
230 | 228 | clock_gettime(CLOCK_REALTIME, &ts); |
@@ -280,6 +278,7 @@ static const char * s_finalResponsesError[] = { | ||
280 | 278 | "NO CARRIER", /* Sometimes! */ |
281 | 279 | "NO ANSWER", |
282 | 280 | "NO DIALTONE", |
281 | + "COMMAND NOT SUPPORT", | |
283 | 282 | }; |
284 | 283 | |
285 | 284 | static int isFinalResponseError(const char *line) |
@@ -425,7 +424,7 @@ static void processLine(const char *line) | ||
425 | 424 | * |
426 | 425 | * returns NULL if there is no complete line. |
427 | 426 | */ |
428 | -static char * findNextEOL(char *cur) | |
427 | +static char *findNextEOL(char *cur) | |
429 | 428 | { |
430 | 429 | if (cur[0] == '>' && cur[1] == ' ' && cur[2] == '\0') { |
431 | 430 | /* SMS prompt character...not \r terminated */ |
@@ -458,7 +457,7 @@ static const char *readline(void) | ||
458 | 457 | char *ret = NULL; |
459 | 458 | |
460 | 459 | struct atcontext *ac = getAtContext(); |
461 | - read(ac->fd,NULL,0); | |
460 | + read(ac->fd, NULL, 0); | |
462 | 461 | |
463 | 462 | /* This is a little odd. I use *s_ATBufferCur == 0 to mean |
464 | 463 | * "buffer consumed completely". If it points to a character, |
@@ -473,8 +472,9 @@ static const char *readline(void) | ||
473 | 472 | /* There's data in the buffer from the last read. */ |
474 | 473 | |
475 | 474 | /* skip over leading newlines */ |
476 | - while (*ac->ATBufferCur == '\r' || *ac->ATBufferCur == '\n') | |
475 | + while (*ac->ATBufferCur == '\r' || *ac->ATBufferCur == '\n') { | |
477 | 476 | ac->ATBufferCur++; |
477 | + } | |
478 | 478 | |
479 | 479 | p_eol = findNextEOL(ac->ATBufferCur); |
480 | 480 |
@@ -540,14 +540,14 @@ static const char *readline(void) | ||
540 | 540 | if (!(pfds[0].revents & POLLIN)) |
541 | 541 | continue; |
542 | 542 | |
543 | - do | |
543 | + do { | |
544 | 544 | /* The size argument should be synchronized to the ditch buffer |
545 | 545 | * condition above. |
546 | 546 | */ |
547 | 547 | count = read(ac->fd, p_read, |
548 | 548 | MAX_AT_RESPONSE - (p_read - ac->ATBuffer) - 2); |
549 | 549 | |
550 | - while (count < 0 && errno == EINTR); | |
550 | + } while (count < 0 && errno == EINTR); | |
551 | 551 | |
552 | 552 | if (count > 0) { |
553 | 553 | AT_DUMP( "<< ", p_read, count ); |
@@ -626,7 +626,7 @@ static void *readerLoop(void *arg) | ||
626 | 626 | if (line == NULL) |
627 | 627 | break; |
628 | 628 | |
629 | - if(isSMSUnsolicited(line)) { | |
629 | + if (isSMSUnsolicited(line)) { | |
630 | 630 | char *line1; |
631 | 631 | const char *line2; |
632 | 632 |
@@ -645,9 +645,10 @@ static void *readerLoop(void *arg) | ||
645 | 645 | ac->unsolHandler(line1, line2); |
646 | 646 | |
647 | 647 | free(line1); |
648 | - } else | |
648 | + } else { | |
649 | 649 | processLine(line); |
650 | 650 | } |
651 | + } | |
651 | 652 | |
652 | 653 | onReaderClosed(); |
653 | 654 | ALOGI("Exiting readerloop!"); |
@@ -751,21 +752,21 @@ static void clearPendingCommand(void) | ||
751 | 752 | ac->response = NULL; |
752 | 753 | ac->responsePrefix = NULL; |
753 | 754 | ac->smsPDU = NULL; |
754 | - } | |
755 | +} | |
755 | 756 | |
756 | 757 | static AT_Error merror(int type, int error) |
757 | 758 | { |
758 | 759 | switch(type) { |
759 | - case AT_ERROR : | |
760 | - return (AT_Error)(AT_ERROR_BASE + error); | |
761 | - case CME_ERROR : | |
762 | - return (AT_Error)(CME_ERROR_BASE + error); | |
763 | - case CMS_ERROR: | |
764 | - return (AT_Error)(CMS_ERROR_BASE + error); | |
765 | - case GENERIC_ERROR: | |
766 | - return (AT_Error)(GENERIC_ERROR_BASE + error); | |
767 | - default: | |
768 | - return (AT_Error)(GENERIC_ERROR_UNSPECIFIED); | |
760 | + case AT_ERROR : | |
761 | + return (AT_Error)(AT_ERROR_BASE + error); | |
762 | + case CME_ERROR : | |
763 | + return (AT_Error)(CME_ERROR_BASE + error); | |
764 | + case CMS_ERROR: | |
765 | + return (AT_Error)(CMS_ERROR_BASE + error); | |
766 | + case GENERIC_ERROR: | |
767 | + return (AT_Error)(GENERIC_ERROR_BASE + error); | |
768 | + default: | |
769 | + return (AT_Error)(GENERIC_ERROR_UNSPECIFIED); | |
769 | 770 | } |
770 | 771 | } |
771 | 772 |
@@ -796,7 +797,7 @@ static AT_Error at_get_error(const ATResponse *p_response) | ||
796 | 797 | if (err < 0) |
797 | 798 | return merror(GENERIC_ERROR, GENERIC_ERROR_UNSPECIFIED); |
798 | 799 | |
799 | - if(strStartsWith(p_response->finalResponse, "+CME ERROR:")) | |
800 | + if (strStartsWith(p_response->finalResponse, "+CME ERROR:")) | |
800 | 801 | return merror(CME_ERROR, ret); |
801 | 802 | else if (strStartsWith(p_response->finalResponse, "+CMS ERROR:")) |
802 | 803 | return merror(CMS_ERROR, ret); |
@@ -902,8 +903,8 @@ void at_response_free(ATResponse *p_response) | ||
902 | 903 | free(p_toFree); |
903 | 904 | } |
904 | 905 | |
905 | - free (p_response->finalResponse); | |
906 | - free (p_response); | |
906 | + free(p_response->finalResponse); | |
907 | + free(p_response); | |
907 | 908 | } |
908 | 909 | |
909 | 910 | /** |
@@ -932,11 +933,10 @@ static void reverseIntermediates(ATResponse *p_response) | ||
932 | 933 | * timeoutMsec == 0 means infinite timeout. |
933 | 934 | */ |
934 | 935 | static int at_send_command_full_nolock (const char *command, ATCommandType type, |
935 | - const char *responsePrefix, const char *smspdu, | |
936 | - long long timeoutMsec, ATResponse **pp_outResponse) | |
936 | + const char *responsePrefix, const char *smspdu, | |
937 | + long long timeoutMsec, ATResponse **pp_outResponse) | |
937 | 938 | { |
938 | 939 | int err = AT_NOERROR; |
939 | - | |
940 | 940 | struct atcontext *ac = getAtContext(); |
941 | 941 | |
942 | 942 | /* Default to NULL, to allow caller to free securely even if |
@@ -962,7 +962,7 @@ static int at_send_command_full_nolock (const char *command, ATCommandType type, | ||
962 | 962 | goto finally; |
963 | 963 | } |
964 | 964 | |
965 | - err = writeline (command); | |
965 | + err = writeline(command); | |
966 | 966 | |
967 | 967 | if (err != AT_NOERROR) |
968 | 968 | goto finally; |
@@ -977,15 +977,15 @@ static int at_send_command_full_nolock (const char *command, ATCommandType type, | ||
977 | 977 | err = AT_ERROR_TIMEOUT; |
978 | 978 | goto finally; |
979 | 979 | } |
980 | - } | |
980 | + } | |
981 | 981 | |
982 | 982 | if (ac->response->success == 0) { |
983 | 983 | err = at_get_error(ac->response); |
984 | 984 | } |
985 | 985 | |
986 | - if (pp_outResponse == NULL) | |
986 | + if (pp_outResponse == NULL) { | |
987 | 987 | at_response_free(ac->response); |
988 | - else { | |
988 | + } else { | |
989 | 989 | /* Line reader stores intermediate responses in reverse order. */ |
990 | 990 | reverseIntermediates(ac->response); |
991 | 991 | *pp_outResponse = ac->response; |
@@ -993,7 +993,7 @@ static int at_send_command_full_nolock (const char *command, ATCommandType type, | ||
993 | 993 | |
994 | 994 | ac->response = NULL; |
995 | 995 | |
996 | - if(ac->readerClosed > 0) { | |
996 | + if (ac->readerClosed > 0) { | |
997 | 997 | err = AT_ERROR_CHANNEL_CLOSED; |
998 | 998 | goto finally; |
999 | 999 | } |
@@ -1013,8 +1013,8 @@ finally: | ||
1013 | 1013 | * timeoutMsec == 0 means infinite timeout. |
1014 | 1014 | */ |
1015 | 1015 | static int at_send_command_full (const char *command, ATCommandType type, |
1016 | - const char *responsePrefix, const char *smspdu, | |
1017 | - long long timeoutMsec, ATResponse **pp_outResponse, int useap, va_list ap) | |
1016 | + const char *responsePrefix, const char *smspdu, | |
1017 | + long long timeoutMsec, ATResponse **pp_outResponse, int useap, va_list ap) | |
1018 | 1018 | { |
1019 | 1019 | int err; |
1020 | 1020 |
@@ -1029,16 +1029,17 @@ static int at_send_command_full (const char *command, ATCommandType type, | ||
1029 | 1029 | pthread_mutex_lock(&ac->commandmutex); |
1030 | 1030 | if (useap) { |
1031 | 1031 | if (!vsnprintf(strbuf, BUFFSIZE, command, ap)) { |
1032 | - pthread_mutex_unlock(&ac->commandmutex); | |
1033 | - return AT_ERROR_STRING_CREATION; | |
1034 | - } | |
1032 | + pthread_mutex_unlock(&ac->commandmutex); | |
1033 | + return AT_ERROR_STRING_CREATION; | |
1034 | + } | |
1035 | 1035 | ptr = strbuf; |
1036 | - } else | |
1036 | + } else { | |
1037 | 1037 | ptr = command; |
1038 | + } | |
1038 | 1039 | |
1039 | 1040 | err = at_send_command_full_nolock(ptr, type, |
1040 | - responsePrefix, smspdu, | |
1041 | - timeoutMsec, pp_outResponse); | |
1041 | + responsePrefix, smspdu, | |
1042 | + timeoutMsec, pp_outResponse); | |
1042 | 1043 | |
1043 | 1044 | pthread_mutex_unlock(&ac->commandmutex); |
1044 | 1045 |
@@ -1054,9 +1055,9 @@ void at_send_escape (void) | ||
1054 | 1055 | struct atcontext *ac = getAtContext(); |
1055 | 1056 | int written; |
1056 | 1057 | |
1057 | - do | |
1058 | + do { | |
1058 | 1059 | written = write (ac->fd, "\033" , 1); |
1059 | - while ((written < 0 && errno == EINTR) || (written == 0)); | |
1060 | + } while ((written < 0 && errno == EINTR) || (written == 0)); | |
1060 | 1061 | } |
1061 | 1062 | |
1062 | 1063 | /** |
@@ -1102,8 +1103,8 @@ int at_send_command_raw (const char *command, ATResponse **pp_outResponse) | ||
1102 | 1103 | } |
1103 | 1104 | |
1104 | 1105 | int at_send_command_singleline (const char *command, |
1105 | - const char *responsePrefix, | |
1106 | - ATResponse **pp_outResponse, ...) | |
1106 | + const char *responsePrefix, | |
1107 | + ATResponse **pp_outResponse, ...) | |
1107 | 1108 | { |
1108 | 1109 | int err; |
1109 | 1110 |
@@ -1111,8 +1112,8 @@ int at_send_command_singleline (const char *command, | ||
1111 | 1112 | va_list ap; |
1112 | 1113 | va_start(ap, pp_outResponse); |
1113 | 1114 | |
1114 | - err = at_send_command_full (command, SINGLELINE, responsePrefix, | |
1115 | - NULL, ac->timeoutMsec, pp_outResponse, 1, ap); | |
1115 | + err = at_send_command_full(command, SINGLELINE, responsePrefix, | |
1116 | + NULL, ac->timeoutMsec, pp_outResponse, 1, ap); | |
1116 | 1117 | |
1117 | 1118 | if (err == AT_NOERROR && pp_outResponse != NULL |
1118 | 1119 | && (*pp_outResponse) != NULL |
@@ -1136,14 +1137,14 @@ int at_send_command_singleline (const char *command, | ||
1136 | 1137 | } |
1137 | 1138 | |
1138 | 1139 | int at_send_command_numeric (const char *command, |
1139 | - ATResponse **pp_outResponse) | |
1140 | + ATResponse **pp_outResponse) | |
1140 | 1141 | { |
1141 | 1142 | int err; |
1142 | 1143 | |
1143 | 1144 | struct atcontext *ac = getAtContext(); |
1144 | 1145 | |
1145 | - err = at_send_command_full (command, NUMERIC, NULL, | |
1146 | - NULL, ac->timeoutMsec, pp_outResponse, 0, empty); | |
1146 | + err = at_send_command_full(command, NUMERIC, NULL, | |
1147 | + NULL, ac->timeoutMsec, pp_outResponse, 0, empty); | |
1147 | 1148 | |
1148 | 1149 | if (err == AT_NOERROR && pp_outResponse != NULL |
1149 | 1150 | && (*pp_outResponse) != NULL |
@@ -1166,16 +1167,16 @@ int at_send_command_numeric (const char *command, | ||
1166 | 1167 | |
1167 | 1168 | |
1168 | 1169 | int at_send_command_sms (const char *command, |
1169 | - const char *pdu, | |
1170 | - const char *responsePrefix, | |
1171 | - ATResponse **pp_outResponse) | |
1170 | + const char *pdu, | |
1171 | + const char *responsePrefix, | |
1172 | + ATResponse **pp_outResponse) | |
1172 | 1173 | { |
1173 | 1174 | int err; |
1174 | 1175 | |
1175 | 1176 | struct atcontext *ac = getAtContext(); |
1176 | 1177 | |
1177 | - err = at_send_command_full (command, SINGLELINE, responsePrefix, | |
1178 | - pdu, ac->timeoutMsec, pp_outResponse, 0, empty); | |
1178 | + err = at_send_command_full(command, SINGLELINE, responsePrefix, | |
1179 | + pdu, ac->timeoutMsec, pp_outResponse, 0, empty); | |
1179 | 1180 | |
1180 | 1181 | if (err == AT_NOERROR && pp_outResponse != NULL |
1181 | 1182 | && (*pp_outResponse) != NULL |
@@ -1198,8 +1199,8 @@ int at_send_command_sms (const char *command, | ||
1198 | 1199 | |
1199 | 1200 | |
1200 | 1201 | int at_send_command_multiline (const char *command, |
1201 | - const char *responsePrefix, | |
1202 | - ATResponse **pp_outResponse, ...) | |
1202 | + const char *responsePrefix, | |
1203 | + ATResponse **pp_outResponse, ...) | |
1203 | 1204 | { |
1204 | 1205 | int err; |
1205 | 1206 |
@@ -1208,7 +1209,7 @@ int at_send_command_multiline (const char *command, | ||
1208 | 1209 | va_start(ap, pp_outResponse); |
1209 | 1210 | |
1210 | 1211 | err = at_send_command_full (command, MULTILINE, responsePrefix, |
1211 | - NULL, ac->timeoutMsec, pp_outResponse, 1, ap); | |
1212 | + NULL, ac->timeoutMsec, pp_outResponse, 1, ap); | |
1212 | 1213 | va_end(ap); |
1213 | 1214 | |
1214 | 1215 | /* Free response in case of error */ |
@@ -1304,7 +1305,7 @@ AT_Error at_get_at_error(int error) | ||
1304 | 1305 | return (AT_Error)(error - AT_ERROR_BASE); |
1305 | 1306 | else |
1306 | 1307 | return AT_ERROR_NON_AT; |
1307 | - } | |
1308 | +} | |
1308 | 1309 | |
1309 | 1310 | AT_CME_Error at_get_cme_error(int error) |
1310 | 1311 | { |
@@ -1313,7 +1314,7 @@ AT_CME_Error at_get_cme_error(int error) | ||
1313 | 1314 | return (AT_CME_Error)(error - CME_ERROR_BASE); |
1314 | 1315 | else |
1315 | 1316 | return CME_ERROR_NON_CME; |
1316 | - } | |
1317 | +} | |
1317 | 1318 | |
1318 | 1319 | AT_CMS_Error at_get_cms_error(int error) |
1319 | 1320 | { |
@@ -1331,7 +1332,7 @@ AT_Generic_Error at_get_generic_error(int error) | ||
1331 | 1332 | return (AT_Generic_Error)(error - GENERIC_ERROR_BASE); |
1332 | 1333 | else |
1333 | 1334 | return GENERIC_ERROR_NON_GENERIC; |
1334 | - } | |
1335 | +} | |
1335 | 1336 | |
1336 | 1337 | AT_Error_type at_get_error_type(int error) |
1337 | 1338 | { |
@@ -1352,7 +1353,7 @@ AT_Error_type at_get_error_type(int error) | ||
1352 | 1353 | return GENERIC_ERROR; |
1353 | 1354 | |
1354 | 1355 | return UNKNOWN_ERROR; |
1355 | - } | |
1356 | +} | |
1356 | 1357 | |
1357 | 1358 | #define quote(x) #x |
1358 | 1359 |
@@ -1361,17 +1362,17 @@ const char *at_str_err(int error) | ||
1361 | 1362 | const char *s = "--UNKNOWN--"; |
1362 | 1363 | |
1363 | 1364 | error = -error; |
1364 | - switch(error) { | |
1365 | + switch (error) { | |
1365 | 1366 | #define AT(name, num) case num + AT_ERROR_BASE: s = quote(AT_##name); break; |
1366 | 1367 | #define CME(name, num) case num + CME_ERROR_BASE: s = quote(CME_##name); break; |
1367 | 1368 | #define CMS(name, num) case num + CMS_ERROR_BASE: s = quote(CMS_##name); break; |
1368 | 1369 | #define GENERIC(name, num) case num + GENERIC_ERROR_BASE: s = quote(GENERIC_##name); break; |
1369 | - mbm_error | |
1370 | + mbm_error | |
1370 | 1371 | #undef AT |
1371 | 1372 | #undef CME |
1372 | 1373 | #undef CMS |
1373 | 1374 | #undef GENERIC |
1374 | -} | |
1375 | + } | |
1375 | 1376 | |
1376 | 1377 | return s; |
1377 | 1378 | } |
@@ -99,17 +99,17 @@ void at_set_on_reader_closed(void (*onClose)(void)); | ||
99 | 99 | void at_send_escape(void); |
100 | 100 | |
101 | 101 | int at_send_command_singleline (const char *command, |
102 | - const char *responsePrefix, | |
103 | - ATResponse **pp_outResponse, | |
104 | - ...); | |
102 | + const char *responsePrefix, | |
103 | + ATResponse **pp_outResponse, | |
104 | + ...); | |
105 | 105 | |
106 | 106 | int at_send_command_numeric (const char *command, |
107 | - ATResponse **pp_outResponse); | |
107 | + ATResponse **pp_outResponse); | |
108 | 108 | |
109 | 109 | int at_send_command_multiline (const char *command, |
110 | - const char *responsePrefix, | |
111 | - ATResponse **pp_outResponse, | |
112 | - ...); | |
110 | + const char *responsePrefix, | |
111 | + ATResponse **pp_outResponse, | |
112 | + ...); | |
113 | 113 | |
114 | 114 | |
115 | 115 | int at_handshake(void); |
@@ -124,8 +124,8 @@ int at_send_command (const char *command, ...); | ||
124 | 124 | int at_send_command_raw (const char *command, ATResponse **pp_outResponse); |
125 | 125 | |
126 | 126 | int at_send_command_sms (const char *command, const char *pdu, |
127 | - const char *responsePrefix, | |
128 | - ATResponse **pp_outResponse); | |
127 | + const char *responsePrefix, | |
128 | + ATResponse **pp_outResponse); | |
129 | 129 | |
130 | 130 | void at_response_free(ATResponse *p_response); |
131 | 131 |
@@ -26,6 +26,16 @@ | ||
26 | 26 | #include <unistd.h> |
27 | 27 | #include <fcntl.h> |
28 | 28 | #include <sys/types.h> |
29 | +#include <sys/epoll.h> | |
30 | +#include <semaphore.h> | |
31 | +#include <signal.h> | |
32 | +#include <linux/socket.h> | |
33 | +#include <sys/socket.h> | |
34 | +#include <errno.h> | |
35 | +#include <stddef.h> | |
36 | +#include <sys/time.h> | |
37 | +#include <sys/select.h> | |
38 | + | |
29 | 39 | #include "audiochannel.h" |
30 | 40 | |
31 | 41 | #define LOG_NDEBUG 0 |
@@ -39,237 +49,568 @@ | ||
39 | 49 | |
40 | 50 | // ---- Android sound streaming ---- |
41 | 51 | |
42 | -/* modemAudioOut: | |
43 | - Output an audio frame (160 samples) to the 3G audio port of the cell modem | |
52 | +/* Audio oversampling factor. To use higher sampling rates reduces latency quite a bit ... */ | |
53 | +#define AUDIO_OVERSAMPLING 1 | |
44 | 54 | |
45 | - data = Pointer to audio data to write | |
55 | +// #define CHECK_MEM_OVERRUN 1 | |
56 | +#define AUDIOCHANNEL_DEBUG 0 | |
57 | +#if AUDIOCHANNEL_DEBUG | |
58 | +# define D(...) ALOGD(__VA_ARGS__) | |
59 | +#else | |
60 | +# define D(...) ((void)0) | |
61 | +#endif | |
46 | 62 | |
47 | -*/ | |
48 | -static int modemAudioOut(struct GsmAudioTunnel* ctx,const void* data) | |
63 | +static inline int labs(int x) | |
49 | 64 | { |
50 | - if (ctx->fd == -1) | |
51 | - return 0; | |
52 | - | |
53 | - // Write audio to the 3G modem audio port in 320 bytes chunks... This is | |
54 | - // required by huawei modems... | |
55 | - | |
56 | - // Write audio chunk | |
57 | - int res = write(ctx->fd, data, ctx->frame_size * (ctx->bits_per_sample/8)); | |
58 | - if (res < 0) | |
59 | - return -1; | |
60 | - | |
61 | - // Write a 0 length frame to post data | |
62 | - res = write(ctx->fd, data, 0); | |
63 | - if (res < 0) | |
64 | - return -1; | |
65 | - | |
66 | - return 0; | |
65 | + return (x < 0) ? -x : x; | |
67 | 66 | } |
68 | 67 | |
69 | -/* modemAudioIn: | |
70 | - Input an audio frame (160 samples) from the 3G audio port of the cell modem | |
71 | - | |
72 | - data = Pointer to buffer where data must be stored | |
68 | +/* modemAudioIOThread: | |
69 | + Output/inputs an audio frame (160 samples) to the 3G audio port of the cell modem | |
70 | + We need to write to be able to read from modem | |
73 | 71 | */ |
74 | -static int modemAudioIn(struct GsmAudioTunnel* ctx, void* data) | |
72 | +static void* modemAudioIOThread(void* data) | |
75 | 73 | { |
74 | + int n; | |
75 | + int max_fd; | |
76 | + fd_set input; | |
77 | + struct timeval timeout; | |
78 | + | |
79 | + struct GsmAudioTunnel* ctx = (struct GsmAudioTunnel*)data; | |
76 | 80 | int res = 0; |
77 | - if (ctx->fd == -1) | |
78 | - return 0; | |
81 | + int bps = (ctx->bits_per_sample/8); | |
82 | + int frame_bytes = ctx->frame_size * bps; | |
83 | + | |
84 | + ALOGD("modemAudioIOThread begin"); | |
85 | + | |
86 | + // Discard all pending data*/ | |
87 | + ALOGD("Discarding old data...."); | |
88 | + tcflush(ctx->fd, TCIOFLUSH); | |
89 | + ALOGD("Discarding old data... Done"); | |
90 | + | |
91 | + // Get audio from the queue and push it into the modem | |
92 | + while (AudioQueue_isrunning(&ctx->rec_q) && | |
93 | + AudioQueue_isrunning(&ctx->play_q)) { | |
94 | + | |
95 | + // Write audio to the 3G modem audio port in 320 bytes chunks... This is | |
96 | + // required by huawei modems... | |
97 | + D("[T]Before AudioQueue_get"); | |
98 | + res = AudioQueue_get(&ctx->rec_q,ctx->play_buf,ctx->frame_size, ctx->timeout); | |
99 | + D("[T]After AudioQueue_get: 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x", | |
100 | + ((short*)ctx->play_buf)[0],((short*)ctx->play_buf)[1], | |
101 | + ((short*)ctx->play_buf)[2],((short*)ctx->play_buf)[3], | |
102 | + ((short*)ctx->play_buf)[4]); | |
103 | + | |
104 | +#ifdef CHECK_MEM_OVERRUN | |
105 | + if (((int*)ctx->rec_buf)[-1] != 0x1A3B5C7D) { | |
106 | + ALOGE("recbuf: Corruption at start: 0x%08x",((int*)ctx->rec_buf)[-1]); | |
107 | + } | |
108 | + if (((int*)ctx->rec_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
109 | + ALOGE("recbuf: Corruption at end: 0x%08x",((int*)ctx->rec_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
110 | + } | |
111 | + if (((int*)ctx->play_buf)[-1] != 0x1A3B5C7D) { | |
112 | + ALOGE("playbuf: Corruption at start: 0x%08x",((int*)ctx->play_buf)[-1]); | |
113 | + } | |
114 | + if (((int*)ctx->play_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
115 | + ALOGE("playbuf: Corruption at end: 0x%08x",((int*)ctx->play_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
116 | + } | |
117 | +#endif | |
118 | + | |
119 | + if (!AudioQueue_isrunning(&ctx->rec_q) || | |
120 | + !AudioQueue_isrunning(&ctx->play_q)) | |
121 | + break; | |
79 | 122 | |
123 | + /* Fill missing samples with silence, if needed */ | |
124 | + if ((int)ctx->frame_size > res) { | |
125 | + memset((char*)ctx->play_buf + res * bps, 0, (ctx->frame_size - res) * bps); | |
126 | + } | |
127 | + | |
128 | +#ifdef CHECK_MEM_OVERRUN | |
129 | + if (((int*)ctx->rec_buf)[-1] != 0x1A3B5C7D) { | |
130 | + ALOGE("recbuf: Corruption at start: 0x%08x",((int*)ctx->rec_buf)[-1]); | |
131 | + } | |
132 | + if (((int*)ctx->rec_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
133 | + ALOGE("recbuf: Corruption at end: 0x%08x",((int*)ctx->rec_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
134 | + } | |
135 | + if (((int*)ctx->play_buf)[-1] != 0x1A3B5C7D) { | |
136 | + ALOGE("playbuf: Corruption at start: 0x%08x",((int*)ctx->play_buf)[-1]); | |
137 | + } | |
138 | + if (((int*)ctx->play_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
139 | + ALOGE("playbuf: Corruption at end: 0x%08x",((int*)ctx->play_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
140 | + } | |
141 | +#endif | |
80 | 142 | |
81 | - while (1) { | |
82 | - res = read(ctx->fd, data, ctx->frame_size * (ctx->bits_per_sample/8)); | |
83 | - if (res == -1) { | |
143 | + // Write audio chunk | |
144 | + D("[T]Before write"); | |
145 | + res = write(ctx->fd, ctx->play_buf, frame_bytes); | |
146 | + D("[T]After write: res: %d",res); | |
84 | 147 | |
85 | - if (errno != EAGAIN && errno != EINTR) { | |
86 | - // A real error, not something that trying again will fix | |
87 | - break; | |
88 | - } | |
89 | - } else { | |
148 | + if (!AudioQueue_isrunning(&ctx->rec_q) || | |
149 | + !AudioQueue_isrunning(&ctx->play_q) || | |
150 | + res < 0) | |
90 | 151 | break; |
91 | - } | |
92 | - } | |
93 | 152 | |
94 | - /* Failure means 0 bytes */ | |
95 | - if (res < 0) | |
96 | - res = 0; | |
153 | + // Read data from the modem | |
154 | + D("[T]Before Select"); | |
97 | 155 | |
98 | - /* If some samples missing, complete them with silence */ | |
99 | - if (res < (int) (ctx->frame_size * (ctx->bits_per_sample/8) )) { | |
156 | + do { | |
157 | + // Initialize the input set | |
158 | + FD_ZERO(&input); | |
159 | + FD_SET(ctx->fd, &input); | |
160 | + max_fd = ctx->fd + 1; | |
100 | 161 | |
101 | - /* Output silence */ | |
102 | - memset( (char*)data + res, 0, (ctx->frame_size * (ctx->bits_per_sample/8) - res)); | |
103 | - } | |
162 | + // Initialize the timeout structure: 40ms is enough for this waiting | |
163 | + timeout.tv_sec = 0; | |
164 | + timeout.tv_usec = 40000; | |
104 | 165 | |
105 | - return 0; | |
106 | -} | |
166 | + // Do the select | |
167 | + n = select(max_fd, &input, NULL, NULL, &timeout); | |
107 | 168 | |
108 | -static void AndroidRecorderCallback(int event, void* userData, void* info) | |
109 | -{ | |
110 | - struct GsmAudioTunnel *ctx = (struct GsmAudioTunnel*) userData; | |
111 | - android::AudioRecord::Buffer* uinfo = (android::AudioRecord::Buffer*) info; | |
112 | - unsigned nsamples; | |
113 | - void *input; | |
169 | + } while (n == 0 && | |
170 | + AudioQueue_isrunning(&ctx->rec_q) && | |
171 | + AudioQueue_isrunning(&ctx->play_q)); | |
114 | 172 | |
115 | - if(!ctx || !uinfo) | |
116 | - return; | |
173 | + D("[T]After Select"); | |
117 | 174 | |
118 | - if (ctx->quit_flag) | |
119 | - goto on_break; | |
175 | + /* See if there was an error */ | |
176 | + if (!AudioQueue_isrunning(&ctx->rec_q) || | |
177 | + !AudioQueue_isrunning(&ctx->play_q) || | |
178 | + n < 0) | |
179 | + break; | |
120 | 180 | |
121 | - input = (void *) uinfo->raw; | |
181 | + /* If something to read, read it */ | |
182 | + if (FD_ISSET(ctx->fd, &input)) { | |
183 | + D("[T]Before read"); | |
184 | + res = read(ctx->fd, ctx->rec_buf, frame_bytes); | |
185 | + D("[T]After read: res: %d",res); | |
122 | 186 | |
123 | - // Calculate number of total samples we've got | |
124 | - nsamples = uinfo->frameCount + ctx->rec_buf_count; | |
187 | + if (!AudioQueue_isrunning(&ctx->play_q) || | |
188 | + !AudioQueue_isrunning(&ctx->rec_q) || | |
189 | + res < 0) | |
190 | + break; | |
125 | 191 | |
126 | - if (nsamples >= ctx->frame_size) { | |
192 | +#ifdef CHECK_MEM_OVERRUN | |
193 | + if (((int*)ctx->rec_buf)[-1] != 0x1A3B5C7D) { | |
194 | + ALOGE("recbuf: Corruption at start: 0x%08x",((int*)ctx->rec_buf)[-1]); | |
195 | + } | |
196 | + if (((int*)ctx->rec_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
197 | + ALOGE("recbuf: Corruption at end: 0x%08x",((int*)ctx->rec_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
198 | + } | |
199 | + if (((int*)ctx->play_buf)[-1] != 0x1A3B5C7D) { | |
200 | + ALOGE("playbuf: Corruption at start: 0x%08x",((int*)ctx->play_buf)[-1]); | |
201 | + } | |
202 | + if (((int*)ctx->play_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
203 | + ALOGE("playbuf: Corruption at end: 0x%08x",((int*)ctx->play_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
204 | + } | |
205 | +#endif | |
127 | 206 | |
128 | - /* If buffer is not empty, combine the buffer with the just incoming | |
129 | - * samples, then call put_frame. | |
130 | - */ | |
131 | - if (ctx->rec_buf_count) { | |
207 | + // If muted, silence audio | |
208 | + if (ctx->ismuted) { | |
209 | + memset( ctx->rec_buf, 0, frame_bytes); | |
210 | + } | |
132 | 211 | |
133 | - unsigned chunk_count = ctx->frame_size - ctx->rec_buf_count; | |
134 | - memcpy( (char*)ctx->rec_buf + ctx->rec_buf_count * (ctx->bits_per_sample/8), input, chunk_count * (ctx->bits_per_sample/8)); | |
212 | +#ifdef CHECK_MEM_OVERRUN | |
213 | + if (((int*)ctx->rec_buf)[-1] != 0x1A3B5C7D) { | |
214 | + ALOGE("recbuf: Corruption at start: 0x%08x",((int*)ctx->rec_buf)[-1]); | |
215 | + } | |
216 | + if (((int*)ctx->rec_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
217 | + ALOGE("recbuf: Corruption at end: 0x%08x",((int*)ctx->rec_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
218 | + } | |
219 | + if (((int*)ctx->play_buf)[-1] != 0x1A3B5C7D) { | |
220 | + ALOGE("playbuf: Corruption at start: 0x%08x",((int*)ctx->play_buf)[-1]); | |
221 | + } | |
222 | + if (((int*)ctx->play_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
223 | + ALOGE("playbuf: Corruption at end: 0x%08x",((int*)ctx->play_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
224 | + } | |
225 | +#endif | |
226 | + | |
227 | + // EM770W firmware corrupts received audio... Try to workaround the damage... | |
228 | + | |
229 | + // 1st Pass: If received less data than requested, this means voice data corruption. | |
230 | + // More frequent than you could think. We must compensate it, or we end with garbled | |
231 | + // voice... | |
232 | + if (res < frame_bytes) { | |
233 | + int p; | |
234 | + signed char* b; | |
235 | + int tf; | |
236 | + | |
237 | + | |
238 | + // Try to reconstruct data . Determine variance of low and high nibbles. | |
239 | + b = (signed char*)ctx->rec_buf; | |
240 | + tf = frame_bytes - res; | |
241 | + for (p = 0; p < 317 && tf!=0; p+=2) { | |
242 | + if (labs(b[p+2] - b[p]) < labs(b[p+1] - b[p+3]) ) { | |
243 | + /* Probably, this is the point ... Insert an space */ | |
244 | + memmove(b+p+1,b+p,320-p-1); | |
245 | + tf--; | |
246 | + p+=2; | |
247 | + } | |
248 | + } | |
249 | + } | |
135 | 250 | |
136 | - /* Send the audio to the modem */ | |
137 | - modemAudioOut(ctx, ctx->rec_buf); | |
251 | + /* 2nd pass: Detect endianness inversions and correct them */ | |
252 | + { | |
253 | + signed short* d = (signed short*)ctx->rec_buf; | |
254 | + signed short ss, sp = 0, s = d[2]; // Handle first sample by reflection | |
255 | + int todo = 160; | |
256 | + | |
257 | + while (todo--) { | |
258 | + sp = s; /* keep previous sample */ | |
259 | + s = *d++; /* Calculate the other possible samples */ | |
260 | + ss = (((unsigned short)s) << 8U) | ((((unsigned short)s) >> 8U) & 0xFFU); | |
261 | + | |
262 | + /* Choose the one that creates less volume difference */ | |
263 | + if (labs(sp - ss ) < labs(sp - s ) ) { | |
264 | + /* Inverted is closer to original. Keep inverted */ | |
265 | + s = ss; | |
266 | + d[-1] = s; | |
267 | + } | |
268 | + } | |
269 | + } | |
138 | 270 | |
139 | - input = (char*) input + chunk_count * (ctx->bits_per_sample/8); | |
140 | - nsamples -= ctx->frame_size; | |
141 | - ctx->rec_buf_count = 0; | |
271 | + /* 3rd pass: Remove clicks - we use a 3 sample window to predict and correct 1-sample clicks...*/ | |
272 | + { | |
273 | + signed short* d = (signed short*)ctx->rec_buf; | |
274 | + signed short spp = 0, sp = *d++, s = *d++; | |
275 | + signed short p; | |
276 | + int todo = 158; | |
277 | + | |
278 | + while (todo--) { | |
279 | + /* Store previous and get new sample */ | |
280 | + spp = sp; | |
281 | + sp = s; | |
282 | + s = *d++; | |
283 | + | |
284 | + /* Estimate medium */ | |
285 | + p = (s + spp) / 2; | |
286 | + | |
287 | + /* If predicted is very different from real, assume noise and replace it */ | |
288 | + if ( labs( sp - p ) > labs(p >> 2) ) { | |
289 | + sp = p; | |
290 | + d[-2] = sp; | |
291 | + } | |
292 | + } | |
293 | + } | |
294 | + | |
295 | + /* 4th pass: Remove 6 Sample clicks... The modem also sometimes creates them. Detect and remove them if possible */ | |
296 | + { | |
297 | + signed short* d = (signed short*)ctx->rec_buf; | |
298 | + signed short sp = 0, s = *d++; | |
299 | + signed short p; | |
300 | + int todo = 154; | |
301 | + | |
302 | + while (todo--) { | |
303 | + /* Store previous and get new sample */ | |
304 | + sp = s; | |
305 | + s = *d++; | |
306 | + | |
307 | + /* If a 4 times jump in value is detected, and 6 samples later we are on track, assume it is a modem generated | |
308 | + click and remove it - We prefer to remove in excess here*/ | |
309 | + if (labs(s) > labs(sp )*4 && | |
310 | + labs(s) > labs(d[6])*4 ) { | |
311 | + | |
312 | + /* Detected an undesired click, remove it! */ | |
313 | + int step = ((d[6] - sp) << (16 - 3)); | |
314 | + int x = sp << 16; | |
315 | + x+= step; | |
316 | + s = d[-1] = x >> 16; | |
317 | + x+= step; | |
318 | + d[ 0] = x >> 16; | |
319 | + x+= step; | |
320 | + d[ 1] = x >> 16; | |
321 | + x+= step; | |
322 | + d[ 2] = x >> 16; | |
323 | + x+= step; | |
324 | + d[ 3] = x >> 16; | |
325 | + x+= step; | |
326 | + d[ 4] = x >> 16; | |
327 | + x+= step; | |
328 | + d[ 5] = x >> 16; | |
329 | + } | |
330 | + } | |
331 | + } | |
332 | + | |
333 | +#if LOG_MODEM_AUDIO | |
334 | + /* Log audio into SD */ | |
335 | + write(ctx->logfd, ctx->rec_buf, frame_bytes); | |
336 | +#endif | |
337 | + | |
338 | + // Write it to the audio queue | |
339 | + D("[T]Before AudioQueue_add: %04x %04x %04x %04x %04x", | |
340 | + ((short*)ctx->rec_buf)[0] & 0xFFFF, | |
341 | + ((short*)ctx->rec_buf)[1] & 0xFFFF, | |
342 | + ((short*)ctx->rec_buf)[2] & 0xFFFF, | |
343 | + ((short*)ctx->rec_buf)[3] & 0xFFFF, | |
344 | + ((short*)ctx->rec_buf)[4] & 0xFFFF); | |
345 | + AudioQueue_add(&ctx->play_q, ctx->rec_buf, ctx->frame_size); | |
346 | + D("[T]After AudioQueue_add"); | |
347 | + | |
348 | +#ifdef CHECK_MEM_OVERRUN | |
349 | + if (((int*)ctx->rec_buf)[-1] != 0x1A3B5C7D) { | |
350 | + ALOGE("recbuf: Corruption at start: 0x%08x",((int*)ctx->rec_buf)[-1]); | |
351 | + } | |
352 | + if (((int*)ctx->rec_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
353 | + ALOGE("recbuf: Corruption at end: 0x%08x",((int*)ctx->rec_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
354 | + } | |
355 | + if (((int*)ctx->play_buf)[-1] != 0x1A3B5C7D) { | |
356 | + ALOGE("playbuf: Corruption at start: 0x%08x",((int*)ctx->play_buf)[-1]); | |
357 | + } | |
358 | + if (((int*)ctx->play_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] != 0xD7C5B3A1) { | |
359 | + ALOGE("playbuf: Corruption at end: 0x%08x",((int*)ctx->play_buf)[(ctx->frame_size * (ctx->bits_per_sample/8))>>2]); | |
360 | + } | |
361 | +#endif | |
142 | 362 | } |
143 | 363 | |
144 | - // Give all frames we have | |
145 | - while (nsamples >= ctx->frame_size) { | |
364 | + }; | |
146 | 365 | |
147 | - /* Send the audio to the modem */ | |
148 | - modemAudioOut(ctx, input); | |
366 | + ALOGD("modemAudioIOThread ended"); | |
367 | + return NULL; | |
368 | +} | |
149 | 369 | |
150 | - input = (char*) input + ctx->frame_size * (ctx->bits_per_sample/8); | |
151 | - nsamples -= ctx->frame_size; | |
152 | - } | |
370 | +/* Called with audio sampled from mic */ | |
371 | +static void AndroidRecorderCallback(int event, void* userData, void* info) | |
372 | +{ | |
373 | + struct GsmAudioTunnel *ctx = (struct GsmAudioTunnel*) userData; | |
374 | + android::AudioRecord::Buffer *uinfo = (android::AudioRecord::Buffer*) info; | |
375 | + int bps, frames; | |
376 | + | |
377 | + if (!ctx || !uinfo || event != android::AudioRecord::EVENT_MORE_DATA) | |
378 | + return; | |
379 | + | |
380 | + if (!AudioQueue_isrunning(&ctx->rec_q)) | |
381 | + goto on_break; | |
153 | 382 | |
154 | - // Store the remaining samples into the buffer | |
155 | - if (nsamples) { | |
156 | - ctx->rec_buf_count = nsamples; | |
157 | - memcpy(ctx->rec_buf, input, nsamples * (ctx->bits_per_sample/8)); | |
383 | + /* Bytes per sample */ | |
384 | + bps = ctx->bits_per_sample/8; | |
385 | + | |
386 | + /* Calculate total frames */ | |
387 | + frames = uinfo->size / bps; | |
388 | + | |
389 | +#if AUDIO_OVERSAMPLING > 1 | |
390 | + /* Subsample */ | |
391 | + { | |
392 | + short *src = (short *)uinfo->raw + frames - 1; | |
393 | + short *dst = (short *)uinfo->raw + frames - 1; | |
394 | + int p; | |
395 | + short v = *src++,vold = 0; | |
396 | + for (p = 0; p < ctx->frame_size-1; p++) { | |
397 | + int x,d,s; | |
398 | + | |
399 | + // Current and target values | |
400 | + vold = v; | |
401 | + v = *src++; | |
402 | + | |
403 | + // Calculate delta and start value | |
404 | + d = (v - vold) * (65536 / AUDIO_OVERSAMPLING); | |
405 | + s = vold << 16; | |
406 | + | |
407 | + // Interpolate values (linear interpolation) | |
408 | + for (x = 0; x < AUDIO_OVERSAMPLING; x++) { | |
409 | + *dst++ = s >> 16; | |
410 | + s += d; | |
411 | + } | |
158 | 412 | } |
159 | 413 | |
160 | - } else { | |
161 | - // Not enough samples, let's just store them in the buffer | |
162 | - memcpy((char*)ctx->rec_buf + ctx->rec_buf_count * (ctx->bits_per_sample/8), input, uinfo->frameCount * (ctx->bits_per_sample/8)); | |
163 | - ctx->rec_buf_count += uinfo->frameCount; | |
414 | + // Last sample can't be interpolated... | |
415 | + for (x = 0; x < AUDIO_OVERSAMPLING; x++) { | |
416 | + *dst++ = v; | |
417 | + } | |
164 | 418 | } |
419 | +#endif | |
165 | 420 | |
421 | + // Post data into the recording queue. Queue should self adapt and adjust sampling rate | |
422 | + D("[A]Before AudioQueue_add"); | |
423 | + uinfo->size = AudioQueue_add(&ctx->rec_q, uinfo->raw, frames) * bps; | |
424 | + D("[A]After AudioQueue_add"); | |
166 | 425 | return; |
167 | 426 | |
168 | 427 | on_break: |
169 | - ALOGD("Record thread stopped"); | |
170 | - ctx->rec_thread_exited = 1; | |
428 | + if (!ctx->rec_thread_exited) { | |
429 | + ALOGD("Record thread stopped"); | |
430 | + ctx->rec_thread_exited = 1; | |
431 | + } | |
171 | 432 | return; |
172 | 433 | } |
173 | 434 | |
435 | +/* Called to get audio samples to playback */ | |
174 | 436 | static void AndroidPlayerCallback( int event, void* userData, void* info) |
175 | 437 | { |
176 | - unsigned nsamples_req; | |
177 | - void *output; | |
178 | 438 | struct GsmAudioTunnel *ctx = (struct GsmAudioTunnel*) userData; |
179 | 439 | android::AudioTrack::Buffer* uinfo = (android::AudioTrack::Buffer*) info; |
440 | + int bps,frames; | |
180 | 441 | |
181 | - if (!ctx || !uinfo) | |
442 | + if (!ctx || !uinfo || event != android::AudioTrack::EVENT_MORE_DATA) | |
182 | 443 | return; |
183 | 444 | |
184 | - if (ctx->quit_flag) | |
445 | + if (!AudioQueue_isrunning(&ctx->play_q)) | |
185 | 446 | goto on_break; |
186 | 447 | |
187 | - nsamples_req = uinfo->frameCount; | |
188 | - output = (void*) uinfo->raw; | |
189 | - | |
190 | - // Check if any buffered samples | |
191 | - if (ctx->play_buf_count) { | |
192 | - | |
193 | - // samples buffered >= requested by sound device | |
194 | - if (ctx->play_buf_count >= nsamples_req) { | |
195 | - | |
196 | - memcpy(output, ctx->play_buf, nsamples_req * (ctx->bits_per_sample/8)); | |
197 | - ctx->play_buf_count -= nsamples_req; | |
198 | - | |
199 | - memmove(ctx->play_buf, | |
200 | - (char*)ctx->play_buf + nsamples_req * (ctx->bits_per_sample/8), ctx->play_buf_count * (ctx->bits_per_sample/8)); | |
201 | - nsamples_req = 0; | |
202 | - return; | |
448 | + // Read data from the Playback audioqueue | |
449 | + D("[A]Before AudioQueue_get"); | |
450 | + bps = ctx->bits_per_sample/8; | |
451 | + uinfo->size = (frames = AudioQueue_get(&ctx->play_q, uinfo->raw, uinfo->size / bps, ctx->timeout)) * bps; | |
452 | + D("[A]After AudioQueue_get"); | |
453 | + | |
454 | +#if AUDIO_OVERSAMPLING > 1 | |
455 | + /* Oversample to send them to the Android playback channel*/ | |
456 | + if (frames) { | |
457 | + short *src = uinfo->raw; | |
458 | + short *dst = uinfo->raw; | |
459 | + int p; | |
460 | + short v = *src++,vold = 0; | |
461 | + for (p = 0; p < frames-1; p++) { | |
462 | + int x,d,s; | |
463 | + | |
464 | + // Current and target values | |
465 | + vold = v; | |
466 | + v = *src++; | |
467 | + | |
468 | + // Calculate delta and start value | |
469 | + d = (v - vold) * (65536 / AUDIO_OVERSAMPLING); | |
470 | + s = vold << 16; | |
471 | + | |
472 | + // Interpolate values (linear interpolation) | |
473 | + for (x = 0; x < AUDIO_OVERSAMPLING; x++) { | |
474 | + *dst++ = s >> 16; | |
475 | + s += d; | |
476 | + } | |
203 | 477 | } |
204 | 478 | |
205 | - // samples buffered < requested by sound device | |
206 | - memcpy(output, ctx->play_buf, | |
207 | - ctx->play_buf_count * (ctx->bits_per_sample/8)); | |
208 | - nsamples_req -= ctx->play_buf_count; | |
209 | - output = (char*)output + ctx->play_buf_count * (ctx->bits_per_sample/8); | |
210 | - ctx->play_buf_count = 0; | |
211 | - } | |
212 | - | |
213 | - // Fill output buffer as requested in chunks | |
214 | - while (nsamples_req) { | |
215 | - if (nsamples_req >= ctx->frame_size) { | |
216 | - | |
217 | - /* get a frame from the modem */ | |
218 | - modemAudioIn(ctx, output); | |
219 | - | |
220 | - nsamples_req -= ctx->frame_size; | |
221 | - output = (char*)output + ctx->frame_size * (ctx->bits_per_sample/8); | |
222 | - | |
223 | - } else { | |
224 | - | |
225 | - /* get a frame from the modem */ | |
226 | - modemAudioIn(ctx, ctx->play_buf); | |
227 | - | |
228 | - memcpy(output, ctx->play_buf, | |
229 | - nsamples_req * (ctx->bits_per_sample/8)); | |
230 | - ctx->play_buf_count = ctx->frame_size - nsamples_req; | |
231 | - memmove(ctx->play_buf, | |
232 | - (char*)ctx->play_buf + nsamples_req * (ctx->bits_per_sample/8), | |
233 | - ctx->play_buf_count * (ctx->bits_per_sample/8)); | |
234 | - nsamples_req = 0; | |
479 | + // Last sample can't be interpolated... | |
480 | + for (x = 0; x < AUDIO_OVERSAMPLING; x++) { | |
481 | + *dst++ = v; | |
235 | 482 | } |
236 | 483 | } |
484 | +#endif | |
237 | 485 | |
238 | 486 | return; |
239 | 487 | |
240 | 488 | on_break: |
241 | - ALOGD("Play thread stopped"); | |
242 | - ctx->play_thread_exited = 1; | |
489 | + if (!ctx->play_thread_exited) { | |
490 | + ALOGD("Play thread stopped"); | |
491 | + ctx->play_thread_exited = 1; | |
492 | + } | |
493 | + | |
494 | + /* Silence output if we are not running */ | |
495 | + memset(uinfo->raw, 0, uinfo->size); | |
243 | 496 | return; |
244 | 497 | } |
245 | 498 | |
246 | 499 | //AT^DDSETEX=2 |
247 | 500 | |
248 | -int gsm_audio_tunnel_start(struct GsmAudioTunnel *ctx,const char* gsmvoicechannel,unsigned int sampling_rate,unsigned int frame_size,unsigned int bits_per_sample) | |
501 | +int gsm_audio_tunnel_start(struct GsmAudioTunnel *ctx, const char *gsmvoicechannel, unsigned int sampling_rate, unsigned int frame_size, unsigned int bits_per_sample) | |
249 | 502 | { |
503 | + pthread_attr_t modem_attr; | |
250 | 504 | struct termios newtio; |
251 | - int format = (bits_per_sample > 8) | |
252 | - ? AUDIO_FORMAT_PCM_16_BIT | |
253 | - : AUDIO_FORMAT_PCM_8_BIT; | |
505 | + int create_result = 0; | |
506 | + size_t playBuffSize = 0; | |
507 | + size_t playNotifyBuffSize = 0; | |
508 | + size_t recBuffSize = 0; | |
509 | + size_t recNotifyBuffSize = 0; | |
510 | + int play_qsize; | |
511 | + int rec_qsize; | |
512 | + | |
513 | + audio_format_t format = (bits_per_sample > 8) | |
514 | + ? AUDIO_FORMAT_PCM_16_BIT | |
515 | + : AUDIO_FORMAT_PCM_8_BIT; | |
254 | 516 | |
255 | 517 | /* If already running, dont do it again */ |
256 | - if (ctx->running) | |
518 | + if (AudioQueue_isrunning(&ctx->rec_q) || | |
519 | + AudioQueue_isrunning(&ctx->play_q)) | |
257 | 520 | return 0; |
258 | 521 | |
259 | 522 | memset(ctx,0,sizeof(struct GsmAudioTunnel)); |
523 | + ctx->fd = -1; | |
260 | 524 | |
261 | 525 | ctx->sampling_rate = sampling_rate; |
262 | 526 | ctx->frame_size = frame_size; |
263 | 527 | ctx->bits_per_sample = bits_per_sample; |
528 | + ctx->timeout = 1000 * frame_size / sampling_rate; | |
264 | 529 | |
265 | 530 | ALOGD("Opening GSM voice channel '%s', sampling_rate:%u hz, frame_size:%u, bits_per_sample:%u ...", |
266 | - gsmvoicechannel,sampling_rate,frame_size,bits_per_sample); | |
531 | + gsmvoicechannel,sampling_rate,frame_size,bits_per_sample); | |
267 | 532 | |
268 | - // Open the device(com port) to be non-blocking (read will return immediately) | |
269 | - ctx->fd = open(gsmvoicechannel, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); | |
533 | +#if LOG_MODEM_AUDIO | |
534 | + ctx->logfd = open("/sdcard/modemlog", O_RDWR); | |
535 | +#endif | |
536 | + | |
537 | + // Compute buffer sizes for record and playback | |
538 | +#if 0 | |
539 | + playBuffSize = 0; | |
540 | + android::AudioSystem::getInputBufferSize( | |
541 | + ctx->sampling_rate * AUDIO_OVERSAMPLING, // Samples per second | |
542 | + format, | |
543 | + AUDIO_CHANNEL_IN_MONO, | |
544 | + &playBuffSize); | |
545 | + recBuffSize = playBuffSize; | |
546 | +#else | |
547 | + android::AudioRecord::getMinFrameCount((int*)&recBuffSize, | |
548 | + ctx->sampling_rate * AUDIO_OVERSAMPLING, // Samples per second | |
549 | + format, | |
550 | + 1); | |
551 | + | |
552 | + //android::AudioSystem::getInputBufferSize( | |
553 | + // ctx->sampling_rate, // Samples per second | |
554 | + // format, | |
555 | + // AUDIO_CHANNEL_IN_MONO, | |
556 | + // &recBuffSize); | |
557 | + //recBuffSize <<= 1; // Convert to bytes | |
558 | + //recBuffSize <<= 1; // Convert to bytes | |
559 | + //while (recBuffSize < frame_size) recBuffSize <<= 1; | |
560 | + //while (playBuffSize < frame_size) playBuffSize <<= 1; | |
561 | + | |
562 | + android::AudioTrack::getMinFrameCount((int*)&playBuffSize, | |
563 | + AUDIO_STREAM_VOICE_CALL, | |
564 | + ctx->sampling_rate * AUDIO_OVERSAMPLING); // Samples per second | |
565 | + | |
566 | + // Do not accept less than the frame size ... Makes no point going lower than that | |
567 | + if (playBuffSize < frame_size) | |
568 | + playBuffSize = frame_size; | |
569 | + if (recBuffSize < frame_size) | |
570 | + recBuffSize = frame_size; | |
571 | + | |
572 | + // Compute minimum playback queue size as a power of 2 | |
573 | + play_qsize = 0; | |
574 | + while ((1 << play_qsize) < playBuffSize) play_qsize++; | |
575 | + play_qsize += 2; // And quad it | |
576 | + | |
577 | + // Compute minimum record queue size as a power of 2 | |
578 | + rec_qsize = 0; | |
579 | + while ((1 << rec_qsize) < recBuffSize) rec_qsize++; | |
580 | + rec_qsize += 2; // And quad it | |
581 | + | |
582 | +#endif | |
583 | + | |
584 | + // We use 2 * size of input/output buffer for ping pong use of record/playback buffers. | |
585 | + playNotifyBuffSize = playBuffSize; | |
586 | + playBuffSize <<= 1; | |
587 | + recNotifyBuffSize = recBuffSize; | |
588 | + recBuffSize <<= 1; | |
589 | + ALOGD("play samples: %d [q:%d], record samples: %d [q:%d]", | |
590 | + playNotifyBuffSize, play_qsize, recNotifyBuffSize, rec_qsize); | |
591 | + | |
592 | + // Init the audioqueues | |
593 | + if (AudioQueue_init(&ctx->play_q,play_qsize,bits_per_sample>>3) < 0) { | |
594 | + ALOGE("Could not init Playback AudioQueue"); | |
595 | + goto error; | |
596 | + } | |
597 | + if (AudioQueue_init(&ctx->rec_q,rec_qsize,bits_per_sample>>3) < 0) { | |
598 | + ALOGE("Could not init Record AudioQueue"); | |
599 | + goto error; | |
600 | + } | |
601 | + | |
602 | + ALOGD("Opening voice channel...."); | |
603 | + | |
604 | +/* if (echocancel_init(&ctx->echo, recNotifyBuffSize ) < 0) { | |
605 | + ALOGE("Could not init Record AudioQueue"); | |
606 | + goto error; | |
607 | + } | |
608 | +*/ | |
609 | + // Open the device(com port) in blocking mode | |
610 | + ctx->fd = open(gsmvoicechannel, O_RDWR | O_NOCTTY); | |
270 | 611 | if (ctx->fd < 0) { |
271 | 612 | ALOGE("Could not open '%s'",gsmvoicechannel); |
272 | - return -1; | |
613 | + goto error; | |
273 | 614 | } |
274 | 615 | |
275 | 616 | // Configure it to get data as raw as possible |
@@ -278,122 +619,108 @@ int gsm_audio_tunnel_start(struct GsmAudioTunnel *ctx,const char* gsmvoicechanne | ||
278 | 619 | newtio.c_iflag = IGNPAR | IGNBRK | IGNCR | IXOFF; |
279 | 620 | newtio.c_oflag = 0; |
280 | 621 | newtio.c_lflag = 0; |
281 | - newtio.c_cc[VMIN]=0; | |
282 | - newtio.c_cc[VTIME]=5; // You may want to tweak this; 5 = 1/2 second, 10 = 1 second | |
622 | + newtio.c_cc[VMIN]=1; | |
623 | + newtio.c_cc[VTIME]=1; | |
283 | 624 | tcsetattr(ctx->fd,TCSANOW, &newtio); |
284 | 625 | |
285 | 626 | ALOGD("Creating streams...."); |
286 | - ctx->rec_buf = malloc(ctx->frame_size * (ctx->bits_per_sample/8)); | |
627 | +#ifdef CHECK_MEM_OVERRUN | |
628 | + ctx->rec_buf = malloc(8 + ctx->frame_size * (ctx->bits_per_sample/8) * AUDIO_OVERSAMPLING); | |
287 | 629 | if (!ctx->rec_buf) { |
288 | - close(ctx->fd); | |
289 | - return -1; | |
630 | + ALOGE("Failed to allocate buffer for playback"); | |
631 | + goto error; | |
290 | 632 | } |
291 | 633 | |
292 | - ctx->play_buf = malloc(ctx->frame_size * (ctx->bits_per_sample/8)); | |
634 | + ctx->play_buf = malloc(8 + ctx->frame_size * (ctx->bits_per_sample/8) * AUDIO_OVERSAMPLING); | |
293 | 635 | if (!ctx->play_buf) { |
294 | - free(ctx->rec_buf); | |
295 | - close(ctx->fd); | |
296 | - return -1; | |
636 | + ALOGE("Failed to allocate buffer for record"); | |
637 | + goto error; | |
297 | 638 | } |
298 | 639 | |
299 | - // Compute buffer size | |
300 | - size_t inputBuffSize = 0; | |
301 | - android::AudioSystem::getInputBufferSize( | |
302 | - ctx->sampling_rate, // Samples per second | |
303 | - format, | |
304 | - AUDIO_CHANNEL_IN_MONO, | |
305 | - &inputBuffSize); | |
640 | + ctx->rec_buf = (int*)ctx->rec_buf + 1; | |
641 | + ((int*)ctx->rec_buf)[-1] = 0x1A3B5C7D; | |
642 | + ((int*)ctx->rec_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] = 0xD7C5B3A1; | |
643 | + ctx->play_buf = (int*)ctx->play_buf + 1; | |
644 | + ((int*)ctx->play_buf)[-1] = 0x1A3B5C7D; | |
645 | + ((int*)ctx->play_buf)[(ctx->frame_size * AUDIO_OVERSAMPLING * (ctx->bits_per_sample/8))>>2] = 0xD7C5B3A1; | |
306 | 646 | |
307 | - // We use 2* size of input buffer for ping pong use of record buffer. | |
308 | - inputBuffSize = 2 * inputBuffSize; | |
647 | +#else | |
648 | + ctx->rec_buf = malloc(ctx->frame_size * (ctx->bits_per_sample/8) * AUDIO_OVERSAMPLING); | |
649 | + if (!ctx->rec_buf) { | |
650 | + ALOGE("Failed to allocate buffer for playback"); | |
651 | + goto error; | |
652 | + } | |
653 | + | |
654 | + ctx->play_buf = malloc(ctx->frame_size * (ctx->bits_per_sample/8) * AUDIO_OVERSAMPLING); | |
655 | + if (!ctx->play_buf) { | |
656 | + ALOGE("Failed to allocate buffer for record"); | |
657 | + goto error; | |
658 | + } | |
659 | +#endif | |
309 | 660 | |
310 | 661 | // Create audio record channel |
311 | 662 | ctx->rec_strm = new android::AudioRecord(); |
312 | 663 | if(!ctx->rec_strm) { |
313 | 664 | ALOGE("fail to create audio record"); |
314 | - free(ctx->play_buf); | |
315 | - free(ctx->rec_buf); | |
316 | - close(ctx->fd); | |
317 | - return -1; | |
665 | + goto error; | |
666 | + } | |
667 | + | |
668 | + // Create audio playback channel | |
669 | + ctx->play_strm = new android::AudioTrack(); | |
670 | + if(!ctx->play_strm) { | |
671 | + ALOGE("Failed to create AudioTrack"); | |
672 | + goto error; | |
318 | 673 | } |
319 | 674 | |
320 | 675 | // Unmute microphone |
321 | 676 | // android::AudioSystem::muteMicrophone(false); |
322 | - int create_result = ((android::AudioRecord*)ctx->rec_strm)->set( | |
323 | - AUDIO_SOURCE_MIC, | |
324 | - ctx->sampling_rate, | |
325 | - format, | |
326 | - AUDIO_CHANNEL_IN_MONO, | |
327 | - inputBuffSize, | |
328 | - 0, //flags | |
329 | - &AndroidRecorderCallback, | |
330 | - (void *) ctx, | |
331 | - (inputBuffSize / 2), // Notification frames | |
332 | - false, | |
333 | - 0); | |
677 | + create_result = ((android::AudioRecord*)ctx->rec_strm)->set( | |
678 | + AUDIO_SOURCE_MIC, | |
679 | + ctx->sampling_rate * AUDIO_OVERSAMPLING, | |
680 | + format, | |
681 | + AUDIO_CHANNEL_IN_MONO, | |
682 | + recBuffSize, | |
683 | + &AndroidRecorderCallback, | |
684 | + (void *) ctx, | |
685 | + recNotifyBuffSize, // Notification frames | |
686 | + false, | |
687 | + 0); | |
334 | 688 | |
335 | 689 | if(create_result != android::NO_ERROR){ |
336 | 690 | ALOGE("fail to check audio record : error code %d", create_result); |
337 | - delete ((android::AudioRecord*)ctx->rec_strm); | |
338 | - free(ctx->play_buf); | |
339 | - free(ctx->rec_buf); | |
340 | - close(ctx->fd); | |
341 | - return -1; | |
691 | + goto error; | |
342 | 692 | } |
343 | 693 | |
344 | 694 | if(((android::AudioRecord*)ctx->rec_strm)->initCheck() != android::NO_ERROR) { |
345 | - ALOGE("fail to check audio record : buffer size is : %d, error code : %d", inputBuffSize, ((android::AudioRecord*)ctx->rec_strm)->initCheck() ); | |
346 | - delete ((android::AudioRecord*)ctx->rec_strm); | |
347 | - free(ctx->play_buf); | |
348 | - free(ctx->rec_buf); | |
349 | - close(ctx->fd); | |
350 | - return -1; | |
351 | - } | |
352 | - | |
353 | - // Create audio playback channel | |
354 | - ctx->play_strm = new android::AudioTrack(); | |
355 | - if(!ctx->play_strm) { | |
356 | - ALOGE("Failed to create AudioTrack"); | |
357 | - delete ((android::AudioRecord*)ctx->rec_strm); | |
358 | - free(ctx->play_buf); | |
359 | - free(ctx->rec_buf); | |
360 | - close(ctx->fd); | |
361 | - return -1; | |
695 | + ALOGE("fail to check audio record : buffer size is : %d, error code : %d", | |
696 | + recBuffSize, ((android::AudioRecord*)ctx->rec_strm)->initCheck()); | |
697 | + goto error; | |
362 | 698 | } |
363 | 699 | |
364 | 700 | // android::AudioSystem::setMasterMute(false); |
365 | 701 | create_result = ((android::AudioTrack*)ctx->play_strm)->set( |
366 | - AUDIO_STREAM_VOICE_CALL, | |
367 | - ctx->sampling_rate, //this is sample rate in Hz (16000 Hz for example) | |
368 | - format, | |
369 | - AUDIO_CHANNEL_OUT_MONO, //For now this is mono (we expect 1) | |
370 | - inputBuffSize, | |
371 | - 0, //flags | |
372 | - &AndroidPlayerCallback, | |
373 | - (void *) ctx, | |
374 | - (inputBuffSize / 2), | |
375 | - 0, | |
376 | - false, | |
377 | - 0); | |
378 | - | |
379 | - if(create_result != android::NO_ERROR){ | |
702 | + AUDIO_STREAM_VOICE_CALL, | |
703 | + ctx->sampling_rate * AUDIO_OVERSAMPLING, //this is sample rate in Hz (16000 Hz for example) | |
704 | + format, | |
705 | + AUDIO_CHANNEL_OUT_MONO, //For now this is mono (we expect 1) | |
706 | + playBuffSize, | |
707 | + AUDIO_OUTPUT_FLAG_NONE, //flags | |
708 | + &AndroidPlayerCallback, | |
709 | + (void *) ctx, | |
710 | + playNotifyBuffSize, | |
711 | + 0, | |
712 | + false, | |
713 | + 0); | |
714 | + | |
715 | + if (create_result != android::NO_ERROR){ | |
380 | 716 | ALOGE("fail to check audio record : error code %d", create_result); |
381 | - delete ((android::AudioTrack*)ctx->play_strm); | |
382 | - delete ((android::AudioRecord*)ctx->rec_strm); | |
383 | - free(ctx->play_buf); | |
384 | - free(ctx->rec_buf); | |
385 | - close(ctx->fd); | |
386 | - return -1; | |
717 | + goto error; | |
387 | 718 | } |
388 | 719 | |
389 | 720 | if(((android::AudioTrack*)ctx->play_strm)->initCheck() != android::NO_ERROR) { |
390 | - ALOGE("fail to check audio playback : buffer size is : %d, error code : %d", inputBuffSize, ((android::AudioTrack*)ctx->play_strm)->initCheck() ); | |
391 | - delete ((android::AudioTrack*)ctx->play_strm); | |
392 | - delete ((android::AudioRecord*)ctx->rec_strm); | |
393 | - free(ctx->play_buf); | |
394 | - free(ctx->rec_buf); | |
395 | - close(ctx->fd); | |
396 | - return -1; | |
721 | + ALOGE("fail to check audio playback : buffer size is : %d, error code : %d", | |
722 | + playBuffSize, ((android::AudioTrack*)ctx->play_strm)->initCheck()); | |
723 | + goto error; | |
397 | 724 | } |
398 | 725 | |
399 | 726 | /* Save the current audio routing setting, then switch it to earpiece. */ |
@@ -413,44 +740,85 @@ int gsm_audio_tunnel_start(struct GsmAudioTunnel *ctx,const char* gsmvoicechanne | ||
413 | 740 | ((android::AudioRecord*)ctx->rec_strm)->start(); |
414 | 741 | } |
415 | 742 | |
416 | - ALOGD("Done"); | |
743 | + // Create the playback thread | |
744 | + pthread_attr_init(&modem_attr); | |
745 | + if (pthread_create(&ctx->modem_t,&modem_attr,modemAudioIOThread,ctx) < 0) { | |
746 | + ALOGE("Failed to start modemAudioIO Thread"); | |
747 | +error: | |
748 | + AudioQueue_end(&ctx->rec_q); | |
749 | + AudioQueue_end(&ctx->play_q); | |
750 | + if (ctx->play_strm) delete ((android::AudioTrack*)ctx->play_strm); | |
751 | + if (ctx->rec_strm) delete ((android::AudioRecord*)ctx->rec_strm); | |
752 | +#ifdef CHECK_MEM_OVERRUN | |
753 | + if (ctx->play_buf) free(((int*)ctx->play_buf)-1); | |
754 | + if (ctx->rec_buf) free(((int*)ctx->rec_buf)-1); | |
755 | +#else | |
756 | + if (ctx->play_buf) free(ctx->play_buf); | |
757 | + if (ctx->rec_buf) free(ctx->rec_buf); | |
758 | +#endif | |
759 | + if (ctx->fd) close(ctx->fd); | |
760 | +#if LOG_MODEM_AUDIO | |
761 | + if (ctx->logfd) close(ctx->logfd); | |
762 | +#endif | |
417 | 763 | |
418 | - ctx->running = 1; | |
764 | + return -1; | |
765 | + } | |
766 | + | |
767 | + ALOGD("Done"); | |
419 | 768 | |
420 | 769 | // OK, done |
421 | 770 | return 0; |
422 | 771 | } |
423 | 772 | |
773 | +/* API: mute audio record channel */ | |
774 | +int gsm_audio_tunnel_mute(struct GsmAudioTunnel *ctx, int muteit) | |
775 | +{ | |
776 | + ctx->ismuted = muteit; | |
777 | + return 0; | |
778 | +} | |
779 | + | |
780 | +/* API: query if tunnel is running */ | |
781 | +int gsm_audio_tunnel_running(struct GsmAudioTunnel *ctx) | |
782 | +{ | |
783 | + if (AudioQueue_isrunning(&ctx->rec_q) || | |
784 | + AudioQueue_isrunning(&ctx->play_q)) | |
785 | + return 1; | |
786 | + return 0; | |
787 | +} | |
788 | + | |
424 | 789 | /* API: destroy ctx. */ |
425 | 790 | int gsm_audio_tunnel_stop(struct GsmAudioTunnel *ctx) |
426 | 791 | { |
427 | - int i = 0; | |
792 | + int i; | |
428 | 793 | |
429 | 794 | /* If not running, dont do it again */ |
430 | - if (!ctx->running) | |
795 | + if (!AudioQueue_isrunning(&ctx->rec_q) || | |
796 | + !AudioQueue_isrunning(&ctx->play_q)) | |
431 | 797 | return 0; |
432 | 798 | |
799 | + ALOGD("Signal all audio threads to stop"); | |
800 | + AudioQueue_end(&ctx->play_q); | |
801 | + AudioQueue_end(&ctx->rec_q); | |
433 | 802 | |
434 | - ALOGD("Will Stop ctx, wait for all audio callback clean"); | |
435 | - ctx->quit_flag = 1; | |
436 | - for (i=0; !ctx->rec_thread_exited && i<100; ++i){ | |
437 | - usleep(100000); | |
438 | - } | |
439 | - for (i=0; !ctx->play_thread_exited && i<100; ++i){ | |
803 | + // Wait until android audio threads are "idling" | |
804 | + for (i=0; (!ctx->rec_thread_exited || !ctx->play_thread_exited) && i<100; ++i) { | |
440 | 805 | usleep(100000); |
441 | 806 | } |
807 | + // After all sleep for 0.2 seconds since android device can be slow | |
808 | + usleep(200000); | |
809 | + ALOGD("Android audio threads are idle"); | |
442 | 810 | |
443 | - // After all sleep for 0.1 seconds since android device can be slow | |
444 | - usleep(100000); | |
445 | - | |
446 | - ALOGD("Stopping ctx.."); | |
447 | 811 | if (ctx->rec_strm) { |
448 | 812 | ((android::AudioRecord*)ctx->rec_strm)->stop(); |
449 | 813 | } |
450 | - | |
451 | 814 | if (ctx->play_strm) { |
452 | 815 | ((android::AudioTrack*)ctx->play_strm)->stop(); |
453 | 816 | } |
817 | + usleep(200000); | |
818 | + ALOGD("Stopped android audio streaming"); | |
819 | + | |
820 | + pthread_join(ctx->modem_t,NULL); | |
821 | + ALOGD("End modemIO thread"); | |
454 | 822 | |
455 | 823 | // Restore the audio routing setting |
456 | 824 | // android::AudioSystem::setRouting(ctx->saved_audio_mode, |
@@ -460,20 +828,21 @@ int gsm_audio_tunnel_stop(struct GsmAudioTunnel *ctx) | ||
460 | 828 | |
461 | 829 | ALOGD("Closing streaming"); |
462 | 830 | |
463 | - if (ctx->play_strm) { | |
464 | - delete ((android::AudioTrack*)ctx->play_strm); | |
465 | - ctx->play_strm = NULL; | |
466 | - } | |
467 | - | |
468 | - if (ctx->rec_strm) { | |
469 | - delete ((android::AudioRecord*)ctx->rec_strm); | |
470 | - ctx->rec_strm = NULL; | |
471 | - } | |
472 | - | |
473 | - free(ctx->play_buf); | |
474 | - free(ctx->rec_buf); | |
831 | + if (ctx->play_strm) delete ((android::AudioTrack*)ctx->play_strm); | |
832 | + if (ctx->rec_strm) delete ((android::AudioRecord*)ctx->rec_strm); | |
833 | +#ifdef CHECK_MEM_OVERRUN | |
834 | + if (ctx->play_buf) free(((int*)ctx->play_buf)-1); | |
835 | + if (ctx->rec_buf) free(((int*)ctx->rec_buf)-1); | |
836 | +#else | |
837 | + if (ctx->play_buf) free(ctx->play_buf); | |
838 | + if (ctx->rec_buf) free(ctx->rec_buf); | |
839 | +#endif | |
840 | + if (ctx->fd) close(ctx->fd); | |
841 | + | |
842 | +#if LOG_MODEM_AUDIO | |
843 | + if (ctx->logfd) close(ctx->logfd); | |
844 | +#endif | |
475 | 845 | |
476 | - close(ctx->fd); | |
477 | 846 | memset(ctx,0,sizeof(struct GsmAudioTunnel)); |
478 | 847 | |
479 | 848 | ALOGD("Done"); |
@@ -1,6 +1,6 @@ | ||
1 | 1 | /* |
2 | 2 | ** |
3 | -** Copyright (C) 2010 Eduardo José Tagle <ejtagle@tutopia.com> | |
3 | +** Copyright (C) 2012 Eduardo José Tagle <ejtagle@tutopia.com> | |
4 | 4 | ** |
5 | 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); |
6 | 6 | ** you may not use this file except in compliance with the License. |
@@ -14,50 +14,67 @@ | ||
14 | 14 | ** See the License for the specific language governing permissions and |
15 | 15 | ** limitations under the License. |
16 | 16 | ** |
17 | -** Author: Christian Bejram <christian.bejram@stericsson.com> | |
18 | 17 | */ |
19 | 18 | #ifndef _AUDIOCHANNEL_H |
20 | 19 | #define _AUDIOCHANNEL_H 1 |
21 | 20 | |
21 | +#include "audioqueue.h" | |
22 | +#include "echocancel.h" | |
23 | +#include <pthread.h> | |
24 | + | |
25 | +#define LOG_MODEM_AUDIO 0 | |
26 | + | |
22 | 27 | struct GsmAudioTunnel { |
23 | - int running; // If running | |
24 | 28 | |
25 | 29 | // 3G voice modem |
26 | 30 | int fd; // Voice data serial port handler |
27 | 31 | |
32 | +#if LOG_MODEM_AUDIO | |
33 | + int logfd; | |
34 | +#endif | |
35 | + | |
28 | 36 | // Common properties |
29 | - volatile int quit_flag; // If threads should quit | |
30 | 37 | unsigned int frame_size; // Frame size |
31 | 38 | unsigned int sampling_rate; // Sampling rate |
32 | 39 | unsigned int bits_per_sample; // Bits per sample. valid values = 16/8 |
40 | + unsigned int timeout; // Maximum wait timeout | |
41 | + pthread_t modem_t; // 3G modem playback/record thread | |
42 | + int ismuted; // If audio record is muted | |
33 | 43 | |
34 | 44 | // Playback |
35 | 45 | void* play_strm; // Playback stream |
36 | 46 | volatile int play_thread_exited;// If play thread has exited |
37 | 47 | void* play_buf; // Pointer to the playback buffer |
38 | - unsigned int play_buf_count; // Count of already stored samples in the playback buffer | |
48 | + struct AudioQueue play_q; // Audio playback queue | |
39 | 49 | |
40 | 50 | // Record |
41 | 51 | void* rec_strm; // Record stream |
42 | 52 | volatile int rec_thread_exited; // If record thread has exited |
43 | 53 | void* rec_buf; // Pointer to the recording buffer |
44 | - unsigned int rec_buf_count; // Count of already stored samples in the recording buffer | |
54 | + struct AudioQueue rec_q; // Audio record queue | |
55 | + | |
56 | + // Echo cancellation | |
57 | + struct echocancel_ctx echo; // Echo cancellator | |
45 | 58 | }; |
46 | 59 | |
47 | -#define GSM_AUDIO_CHANNEL_STATIC_INIT { 0, 0, 0,0,0,0, 0,0,0,0 ,0,0,0,0 } | |
60 | +#define GSM_AUDIO_CHANNEL_STATIC_INIT { -1, 0,0,0,0,0,0, 0,0,0,{0} ,0,0,0,{0}, {0} } | |
48 | 61 | |
49 | 62 | #ifdef __cplusplus |
50 | 63 | extern "C" { |
51 | 64 | #endif |
52 | 65 | |
53 | 66 | int gsm_audio_tunnel_start(struct GsmAudioTunnel *stream, |
54 | - const char* gsmvoicechannel, | |
55 | - unsigned int sampling_rate, | |
56 | - unsigned int frame_size, | |
57 | - unsigned int bits_per_sample); | |
67 | + const char* gsmvoicechannel, | |
68 | + unsigned int sampling_rate, | |
69 | + unsigned int frame_size, | |
70 | + unsigned int bits_per_sample); | |
58 | 71 | |
59 | 72 | int gsm_audio_tunnel_stop(struct GsmAudioTunnel *stream); |
60 | 73 | |
74 | +int gsm_audio_tunnel_running(struct GsmAudioTunnel *ctx); | |
75 | + | |
76 | +int gsm_audio_tunnel_mute(struct GsmAudioTunnel *stream, int muteit); | |
77 | + | |
61 | 78 | #ifdef __cplusplus |
62 | 79 | } |
63 | 80 | #endif |
@@ -0,0 +1,421 @@ | ||
1 | +#include "audioqueue.h" | |
2 | +#include <string.h> | |
3 | +#include <stdio.h> | |
4 | + | |
5 | +#define LOG_NDEBUG 0 | |
6 | +#define LOG_TAG "RILAudioQueue" | |
7 | +#include <utils/Log.h> | |
8 | + | |
9 | +#define f_mul(a,b) (((long long)(a))*(b) >> 28) | |
10 | +#define f_div(a,b) ((((long long)(a)) << 28) / (b)) | |
11 | +#define f_fract(a) ((a) & ((1<<28)-1)) | |
12 | +#define f_intp(a) ((a) >> 28) | |
13 | +#define F_ONE (1<<28) | |
14 | +#define F_NBR(x) ((int)((1<<28) * (x))) | |
15 | + | |
16 | +//#define CHECK_MEM_OVERRUN | |
17 | +#define AUDIOQUEUE_DEBUG 0 | |
18 | +#if AUDIOQUEUE_DEBUG | |
19 | +# define D(...) ALOGD(__VA_ARGS__) | |
20 | +#else | |
21 | +# define D(...) ((void)0) | |
22 | +#endif | |
23 | + | |
24 | +/* Return count in buffer. */ | |
25 | +#define CIRC_CNT(wr_pos,rd_pos,size) (((wr_pos) - (rd_pos)) & ((size)-1)) | |
26 | + | |
27 | +/* Return space available, 0..size-1. We always leave one free char | |
28 | + as a completely full buffer has wr_pos == rd_pos, which is the same as | |
29 | + empty. */ | |
30 | +#define CIRC_SPACE(wr_pos,rd_pos,size) CIRC_CNT((rd_pos),((wr_pos)+1),(size)) | |
31 | + | |
32 | +/* Return count up to the end of the buffer. Carefully avoid | |
33 | + accessing wr_pos and rd_pos more than once, so they can change | |
34 | + underneath us without returning inconsistent results. */ | |
35 | +#define CIRC_CNT_TO_END(wr_pos,rd_pos,size) \ | |
36 | + ({int end = (size) - (rd_pos); \ | |
37 | + int n = ((wr_pos) + end) & ((size)-1); \ | |
38 | + n < end ? n : end;}) | |
39 | + | |
40 | +/* Return space available up to the end of the buffer. */ | |
41 | +#define CIRC_SPACE_TO_END(wr_pos,rd_pos,size) \ | |
42 | + ({int end = (size) - 1 - (wr_pos); \ | |
43 | + int n = (end + (rd_pos)) & ((size)-1); \ | |
44 | + n <= end ? n : end+1;}) | |
45 | + | |
46 | + | |
47 | + | |
48 | +// Init the audio queue | |
49 | +int AudioQueue_init(struct AudioQueue* ctx,unsigned int p2maxsamples, unsigned int sample_sz) | |
50 | +{ | |
51 | + unsigned int maxsamples = 1U << p2maxsamples; | |
52 | + memset(ctx,0,sizeof(*ctx)); | |
53 | + | |
54 | + ctx->size = maxsamples; | |
55 | +#if NEW_SYNC_ALGO | |
56 | + ctx->waitidx = 1; | |
57 | +#endif | |
58 | + ctx->sample_sz = sample_sz; | |
59 | + | |
60 | + /* Linear resampler */ | |
61 | + ctx->ratio = F_ONE; | |
62 | + | |
63 | + ALOGD("[%p] Initializing audio queue: size: %d, low: %d, high: %d, sample_sz: %d", | |
64 | + ctx,ctx->size,ctx->low,ctx->high,ctx->sample_sz); | |
65 | + | |
66 | +#ifdef CHECK_MEM_OVERRUN | |
67 | + ctx->data = malloc(8 + maxsamples * sample_sz); | |
68 | + if (!ctx->data) { | |
69 | + ALOGE("{%p} Failed to allocate %d memory",ctx, 8 + maxsamples * sample_sz); | |
70 | + return -1; | |
71 | + } | |
72 | + ctx->data = (int*)ctx->data + 1; | |
73 | + ((int*)ctx->data)[-1] = 0x1A2B6C7D; | |
74 | + ((int*)ctx->data)[(ctx->size*ctx->sample_sz)>>2] = 0xD7C6B2A1; | |
75 | +#else | |
76 | + ctx->data = malloc(maxsamples * sample_sz); | |
77 | + if (!ctx->data) { | |
78 | + ALOGE("{%p} Failed to allocate %d memory",ctx, maxsamples * sample_sz); | |
79 | + return -1; | |
80 | + } | |
81 | +#endif | |
82 | + | |
83 | + // Init audio AGC | |
84 | + agc_init(&ctx->agc, 31000); | |
85 | + | |
86 | + ctx->running = 1; | |
87 | + | |
88 | + return 0; | |
89 | +} | |
90 | + | |
91 | +int AudioQueue_isrunning(struct AudioQueue* ctx) | |
92 | +{ | |
93 | + return ctx->running; | |
94 | +} | |
95 | + | |
96 | +int AudioQueue_add(struct AudioQueue* ctx, void* data,unsigned int samples) | |
97 | +{ | |
98 | + unsigned int samples_todo = samples; | |
99 | + | |
100 | + // If exited, avoid adding | |
101 | + if (!ctx->running) | |
102 | + return 0; | |
103 | + | |
104 | + D("add[%p]: begin: Store %d samples",ctx,samples); | |
105 | + | |
106 | + // Not filled, add to the queue. | |
107 | + while (ctx->running && samples_todo) { | |
108 | + | |
109 | + // Calculate remaining space until end of buffer. We always leave a byte free | |
110 | + // so we can differenciate between empty and full buffers | |
111 | + unsigned int rem = CIRC_SPACE_TO_END(ctx->wr_pos,ctx->rd_pos,ctx->size); | |
112 | + | |
113 | + D("add[%p]: samples_todo: %u, rem: %u, rd: %u, wr: %u, sz: %u", | |
114 | + ctx, samples_todo, rem, ctx->rd_pos, ctx->wr_pos, ctx->size); | |
115 | + | |
116 | + if (rem == 0) { | |
117 | + /* not enough data... Ignore the part we can't store */ | |
118 | + D("add[%p]: Not enough space on queue...",ctx); | |
119 | + break; | |
120 | + | |
121 | + } else { | |
122 | + // Do not store more than needed | |
123 | + if (rem > samples_todo) { | |
124 | + rem = samples_todo; | |
125 | + } | |
126 | + | |
127 | + // Store data in queue | |
128 | + memcpy( (char*)ctx->data + ctx->wr_pos * ctx->sample_sz, data, rem * ctx->sample_sz); | |
129 | + data = (char*) data + rem * ctx->sample_sz; | |
130 | + ctx->wr_pos = (ctx->wr_pos + rem) & (ctx->size-1); | |
131 | + samples_todo -= rem; | |
132 | + } | |
133 | + } | |
134 | + | |
135 | + D("add[%p]: end: Stored %d samples, size %d, rd:%d, wr:%d",ctx,samples - samples_todo,ctx->size, ctx->rd_pos, ctx->wr_pos); | |
136 | + | |
137 | +#if !NEW_SYNC_ALGO | |
138 | + // Adjust ratio if queue is getting full, to keep fullness under control | |
139 | + if (ctx->high && CIRC_CNT(ctx->wr_pos,ctx->rd_pos,ctx->size) > ctx->high) { | |
140 | + unsigned int ratio = ctx->ratio; | |
141 | + | |
142 | + // Adjust ratio to avoid this the next time | |
143 | + ratio += ratio/200; | |
144 | + | |
145 | + // Limit to sensible values | |
146 | + if (ratio > F_NBR(1.05)) { | |
147 | + ratio = F_NBR(1.05); | |
148 | + } | |
149 | + ctx->ratio = ratio; | |
150 | + D("add[%p]: Adjusting ratio to keep queue 3/4 full: New ratio: %u",ctx, ratio); | |
151 | + } | |
152 | +#endif | |
153 | + | |
154 | +#ifdef CHECK_MEM_OVERRUN | |
155 | + if (((int*)ctx->data)[-1] != 0x1A2B6C7D) { | |
156 | + ALOGE("add[%p] Memory corruption at start: Found: %08x",ctx, ((int*)ctx->data)[-1]); | |
157 | + } | |
158 | + | |
159 | + if (((int*)ctx->data)[(ctx->size*ctx->sample_sz)>>2] != 0xD7C6B2A1) { | |
160 | + ALOGE("add[%p] Memory corruption at end: Found: %08x",ctx, ((int*)ctx->data)[ctx->size*ctx->sample_sz]); | |
161 | + } | |
162 | +#endif | |
163 | + | |
164 | + // Return the count of stored samples | |
165 | + return samples - samples_todo; | |
166 | +} | |
167 | + | |
168 | +int AudioQueue_get(struct AudioQueue* ctx, void* data,unsigned int samples,unsigned int timeoutms) | |
169 | +{ | |
170 | + unsigned int maxgetreq; | |
171 | + unsigned int samples_todo = samples; | |
172 | + void* pdata = data; | |
173 | +#if NEW_SYNC_ALGO | |
174 | + struct timeval startop; | |
175 | + struct timeval curr; | |
176 | + unsigned int deltatm = 0; | |
177 | +#endif | |
178 | + | |
179 | + // If exited, avoid adding | |
180 | + if (!ctx->running) | |
181 | + return 0; | |
182 | + | |
183 | + D("get[%p]: begin: Read of %d samples",ctx,samples); | |
184 | + | |
185 | +#if !NEW_SYNC_ALGO | |
186 | + /* Keep track of the maximum read size, as we will use it as low/high limit */ | |
187 | + maxgetreq = ctx->maxgetreq; | |
188 | + if (samples > maxgetreq) { | |
189 | + maxgetreq = samples; | |
190 | + ctx->maxgetreq = maxgetreq; | |
191 | + | |
192 | + /* Limit to something we can use */ | |
193 | + if (maxgetreq > ctx->size / 4) { | |
194 | + maxgetreq = ctx->size / 4; | |
195 | + } | |
196 | + | |
197 | + ctx->low = maxgetreq*2; // Low limit: 2 read pending | |
198 | + ctx->high = maxgetreq*3; // High limit: 3 reads pending | |
199 | + | |
200 | + D("get[%p]: new limits: low:%u, high:%u",ctx,ctx->low,ctx->high); | |
201 | + } | |
202 | +#endif | |
203 | + | |
204 | +#if NEW_SYNC_ALGO | |
205 | + // Store the time of the start of this operation | |
206 | + gettimeofday(&startop,NULL); | |
207 | +#endif | |
208 | + | |
209 | + // While samples to be read | |
210 | + while (ctx->running && samples_todo) { | |
211 | + | |
212 | + // Read the first free position | |
213 | + int av = CIRC_CNT(ctx->wr_pos,ctx->rd_pos,ctx->size); | |
214 | + unsigned int step = ctx->step, mask = ctx->size-1; | |
215 | + unsigned int rd_pos = ctx->rd_pos; | |
216 | + | |
217 | + D("get[%p]: [1] samples_todo: %u, rd: %u, wr: %u, sz: %u", | |
218 | + ctx, samples_todo, ctx->rd_pos, ctx->wr_pos, ctx->size); | |
219 | + | |
220 | + // linear interpolation resampling until all requested data is provided | |
221 | + if (ctx->sample_sz == 2) { | |
222 | + | |
223 | + short const *psrc = ctx->data; | |
224 | + short *pdst = (short*)pdata; | |
225 | + | |
226 | + /* We use the current one and next one samples */ | |
227 | + while (samples_todo) { | |
228 | + int ipart = f_intp(step); | |
229 | + rd_pos = (rd_pos + ipart) & mask; | |
230 | + av -= ipart; | |
231 | + step = f_fract(step); | |
232 | + | |
233 | + // If not enough data, break now | |
234 | + if (av < 2) | |
235 | + break; | |
236 | + | |
237 | + *pdst++ = psrc[rd_pos] + f_mul(psrc[(rd_pos+1) & mask] - psrc[rd_pos], step); | |
238 | + samples_todo--; | |
239 | + step += ctx->ratio; | |
240 | + | |
241 | + // Update buffer pointers and linear resampler step | |
242 | + ctx->step = step; | |
243 | + ctx->rd_pos = rd_pos; | |
244 | + } | |
245 | + pdata = pdst; | |
246 | + | |
247 | + } else { | |
248 | + unsigned char const *psrc = ctx->data; | |
249 | + unsigned char *pdst = (unsigned char*)pdata; | |
250 | + | |
251 | + /* We use the current one and next one samples */ | |
252 | + while (samples_todo) { | |
253 | + int ipart = f_intp(step); | |
254 | + rd_pos = (rd_pos + ipart) & mask; | |
255 | + av -= ipart; | |
256 | + step = f_fract(step); | |
257 | + | |
258 | + // If not enough data, break now | |
259 | + if (av < 2) | |
260 | + break; | |
261 | + | |
262 | + *pdst++ = psrc[rd_pos] + f_mul(psrc[(rd_pos+1) & mask] - psrc[rd_pos], step); | |
263 | + samples_todo--; | |
264 | + step += ctx->ratio; | |
265 | + | |
266 | + // Update buffer pointers and linear resampler step | |
267 | + ctx->step = step; | |
268 | + ctx->rd_pos = rd_pos; | |
269 | + } | |
270 | + pdata = pdst; | |
271 | + } | |
272 | + | |
273 | + D("get[%p]: [2] samples_todo: %u, rd: %u, wr: %u, sz: %u", | |
274 | + ctx, samples_todo, ctx->rd_pos, ctx->wr_pos, ctx->size); | |
275 | + if (samples_todo) { | |
276 | + | |
277 | +#if NEW_SYNC_ALGO | |
278 | + /* Get actual time */ | |
279 | + gettimeofday(&curr,NULL); | |
280 | + | |
281 | + /* Yield a bit - 1 ms */ | |
282 | + usleep(1000); | |
283 | + | |
284 | + /* Calculate waiting time */ | |
285 | + deltatm = (curr.tv_sec - startop.tv_sec) * 1000000 + (curr.tv_usec - startop.tv_usec); | |
286 | + | |
287 | + /* If we are allowed to wait a bit to get those samples, keep trying */ | |
288 | + if (deltatm < timeoutms) | |
289 | + continue; | |
290 | +#endif | |
291 | + | |
292 | + // No more samples to provide.... | |
293 | + D("get[%p]: Not enough data on queue...",ctx); | |
294 | + | |
295 | + // Return what we have... | |
296 | + break; | |
297 | + } | |
298 | + }; | |
299 | + | |
300 | + D("get[%p]: end: got %d samples, total: %d, rd:%d, wr:%d", | |
301 | + ctx, samples - samples_todo, ctx->size,ctx->rd_pos, ctx->wr_pos); | |
302 | + | |
303 | +#if NEW_SYNC_ALGO | |
304 | + // The idea is that we want to keep nearly in sync adds with gets, but this is not easy, as | |
305 | + // there is a lot of jitter in the add and get operations. So, instead of that, what we strive | |
306 | + // is to adjust sampling rate so if we had to wait, wait more than 0 but less than 2ms. | |
307 | + | |
308 | + if (deltatm == 0) { | |
309 | + // We didn't had to wait. Everything could be fine, or we could be running the queue | |
310 | + // too slow. try to speed it up a bit: We want to hit the 1ms waiting mark... | |
311 | + | |
312 | + unsigned int ratio = ctx->ratio; | |
313 | + | |
314 | + // Adjust ratio to avoid this the next time. | |
315 | + ratio += ratio * ctx->waitidx / samples; | |
316 | + | |
317 | + // Limit to sensible values | |
318 | + if (ratio > F_NBR(1.05)) { | |
319 | + ratio = F_NBR(1.05); | |
320 | + } | |
321 | + ctx->ratio = ratio; | |
322 | + | |
323 | + // Increment the nowaitctr, as this time we did not had to wait | |
324 | + if (ctx->nowaitctr < 50) | |
325 | + ctx->nowaitctr ++; | |
326 | + | |
327 | + D("get[%p]: Adjusting ratio to try to queue as empty as possible: New ratio: %u",ctx, ratio); | |
328 | + | |
329 | + } else { | |
330 | + // We had to wait. The queue is running too fast. Adjust it so the next time we don´t | |
331 | + // have to wait... | |
332 | + unsigned int waitidx = ctx->waitidx; | |
333 | + unsigned int ratio = ctx->ratio; | |
334 | + ratio -= ratio * deltatm / samples; | |
335 | + | |
336 | + // Limit to sensible values | |
337 | + if (ratio < F_NBR(0.95)) { | |
338 | + ratio = F_NBR(0.95); | |
339 | + } | |
340 | + ctx->ratio = ratio; | |
341 | + | |
342 | + // We had to wait... Adjust the wait index to try to make it nonwait most of the time | |
343 | + if (ctx->nowaitctr < 50) { | |
344 | + if (waitidx > 1) | |
345 | + waitidx --; | |
346 | + } else if (ctx->nowaitctr > 50) { | |
347 | + if (waitidx < 50) | |
348 | + waitidx ++; | |
349 | + } | |
350 | + ctx->waitidx = waitidx; | |
351 | + | |
352 | + // And restart counting | |
353 | + ctx->nowaitctr = 0; | |
354 | + | |
355 | + D("get[%p]: Adjusting rate to try to avoid waiting: New ratio: %u, waitidx: %u",ctx,ratio,waitidx); | |
356 | + } | |
357 | +#endif | |
358 | + | |
359 | +#if !NEW_SYNC_ALGO | |
360 | + // Adjust ratio if queue is getting empty, to keep fullness under control | |
361 | + if (samples != samples_todo && /* Something output */ | |
362 | + ctx->low && /* Limit set */ | |
363 | + CIRC_CNT(ctx->wr_pos,ctx->rd_pos,ctx->size) < ctx->low) { | |
364 | + | |
365 | + unsigned int ratio = ctx->ratio; | |
366 | + ratio -= ratio / 200; | |
367 | + | |
368 | + // Limit to sensible values | |
369 | + if (ratio < F_NBR(0.95)) { | |
370 | + ratio = F_NBR(0.95); | |
371 | + } | |
372 | + ctx->ratio = ratio; | |
373 | + | |
374 | + D("get[%p]: Adjusting rate to keep queue at least 1/4 full: New ratio: %u",ctx,ratio); | |
375 | + } | |
376 | +#endif | |
377 | + | |
378 | + // Apply AGC to the samples | |
379 | + if (ctx->sample_sz == 2 ) { | |
380 | + agc_process_16bit(&ctx->agc,(short*) data, samples - samples_todo); | |
381 | + } else { | |
382 | + agc_process_8bit(&ctx->agc,(unsigned char*) data, samples - samples_todo); | |
383 | + } | |
384 | + | |
385 | +#ifdef CHECK_MEM_OVERRUN | |
386 | + if (((int*)ctx->data)[-1] != 0x1A2B6C7D) { | |
387 | + ALOGE("get[%p] Memory corruption at start: Found: %08x",ctx, ((int*)ctx->data)[-1]); | |
388 | + } | |
389 | + | |
390 | + if (((int*)ctx->data)[(ctx->size*ctx->sample_sz)>>2] != 0xD7C6B2A1) { | |
391 | + ALOGE("get[%p] Memory corruption at end: Found: %08x",ctx, ((int*)ctx->data)[ctx->size*ctx->sample_sz]); | |
392 | + } | |
393 | +#endif | |
394 | + | |
395 | + | |
396 | + // Return the count of read samples | |
397 | + return samples - samples_todo; | |
398 | +} | |
399 | + | |
400 | +int AudioQueue_end(struct AudioQueue* ctx) | |
401 | +{ | |
402 | + | |
403 | + // If exited, avoid reexecuting deinitialization | |
404 | + if (!ctx->running) | |
405 | + return 0; | |
406 | + | |
407 | + // Signal end | |
408 | + ctx->running = 0; | |
409 | + | |
410 | + // Some delay to let add and get end... | |
411 | + sleep(1); | |
412 | + | |
413 | +#ifdef CHECK_MEM_OVERRUN | |
414 | + free(((int*)ctx->data)-1); | |
415 | +#else | |
416 | + free(ctx->data); | |
417 | +#endif | |
418 | + | |
419 | + memset(ctx,0,sizeof(struct AudioQueue)); | |
420 | + return 0; | |
421 | +} |
@@ -0,0 +1,64 @@ | ||
1 | +/* | |
2 | +** | |
3 | +** Copyright (C) 2012 Eduardo José Tagle <ejtagle@tutopia.com> | |
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 | + | |
19 | +/* A resampling Audio queue with AGC */ | |
20 | + | |
21 | +#ifndef __AUDIOQUEUE_H | |
22 | +#define __AUDIOQUEUE_H 1 | |
23 | + | |
24 | +#include <pthread.h> | |
25 | +#include <sys/time.h> | |
26 | +#include "agc.h" | |
27 | + | |
28 | +#define NEW_SYNC_ALGO 0 | |
29 | + | |
30 | +struct AudioQueue { | |
31 | + volatile int running; // != 0 if running | |
32 | + unsigned int size; // Queue size in samples | |
33 | +#if !NEW_SYNC_ALGO | |
34 | + unsigned int maxgetreq; // Maximum request size | |
35 | + unsigned int low; // Low limit | |
36 | + unsigned int high; // High limit | |
37 | +#else | |
38 | + unsigned int nowaitctr; // No waiting counter | |
39 | + unsigned int waitidx; // Wait index | |
40 | +#endif | |
41 | + unsigned int sample_sz; // Sample size in bytes | |
42 | + unsigned int wr_pos; // Write position in samples | |
43 | + unsigned int rd_pos; // Read position in samples | |
44 | + unsigned int ratio; // Resampling ratio including speedup/speeddown due to fullness | |
45 | + unsigned int step; // Linear interpolation step | |
46 | + struct agc_ctx agc; // AGC control | |
47 | + void* data; // Queue Data buffer | |
48 | +}; | |
49 | + | |
50 | +#ifdef __cplusplus | |
51 | +extern "C" { | |
52 | +#endif | |
53 | + | |
54 | +int AudioQueue_init(struct AudioQueue* ctx,unsigned int p2maxsamples, unsigned int sample_sz); | |
55 | +int AudioQueue_isrunning(struct AudioQueue* ctx); | |
56 | +int AudioQueue_add(struct AudioQueue* ctx, void* data,unsigned int samples); | |
57 | +int AudioQueue_get(struct AudioQueue* ctx, void* data,unsigned int samples,unsigned int timeout); | |
58 | +int AudioQueue_end(struct AudioQueue* ctx); | |
59 | + | |
60 | +#ifdef __cplusplus | |
61 | +} | |
62 | +#endif | |
63 | + | |
64 | +#endif | |
\ No newline at end of file |
@@ -0,0 +1,205 @@ | ||
1 | +/* | |
2 | + * Copyrightm (C) 2010 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#include <stdio.h> | |
18 | +#include <string.h> | |
19 | +#include <stdint.h> | |
20 | +#include <string.h> | |
21 | +#include <math.h> | |
22 | + | |
23 | +#define LOG_TAG "EchoCancel" | |
24 | +#include <utils/Log.h> | |
25 | + | |
26 | +#include "echocancel.h" | |
27 | + | |
28 | +// It is very difficult to do echo cancellation at this level due to the lack of | |
29 | +// the timing information of the samples being played and recorded. Therefore, | |
30 | +// for the first release only echo suppression is implemented. | |
31 | + | |
32 | +// The algorithm is derived from the "previous works" summarized in | |
33 | +// A new class of doubletalk detectors based on cross-correlation, | |
34 | +// J Benesty, DR Morgan, JH Cho, IEEE Trans. on Speech and Audio Processing. | |
35 | +// The method proposed in that paper is not used because of its high complexity. | |
36 | + | |
37 | +// It is well known that cross-correlation can be computed using convolution, | |
38 | +// but unfortunately not every mobile processor has a (fast enough) FPU. Thus | |
39 | +// we use integer arithmetic as much as possible and do lots of bookkeeping. | |
40 | +// Again, parameters and thresholds are chosen by experiments. | |
41 | + | |
42 | +int echocancel_init(struct echocancel_ctx* ctx,int sampleCount, int tailLength) | |
43 | +{ | |
44 | + int shift; | |
45 | + | |
46 | + tailLength += sampleCount * 4; | |
47 | + | |
48 | + shift = 0; | |
49 | + while ((sampleCount >> shift) > 1 && (tailLength >> shift) > 256) { | |
50 | + ++shift; | |
51 | + } | |
52 | + | |
53 | + ctx->Shift = shift + 4; | |
54 | + ctx->Scale = 1 << shift; | |
55 | + ctx->SampleCount = sampleCount; | |
56 | + ctx->WindowSize = sampleCount >> shift; | |
57 | + ctx->TailLength = tailLength >> shift; | |
58 | + ctx->RecordLength = tailLength * 2 / sampleCount; | |
59 | + ctx->RecordOffset = 0; | |
60 | + | |
61 | + ctx->Xs = calloc(sizeof(*ctx->Xs), (ctx->TailLength + ctx->WindowSize)); | |
62 | + ctx->XSums = calloc(sizeof(*ctx->XSums), (ctx->TailLength)); | |
63 | + ctx->X2Sums = calloc(sizeof(*ctx->X2Sums), ctx->TailLength); | |
64 | + ctx->XRecords = calloc(sizeof(*ctx->XRecords), (ctx->RecordLength * ctx->WindowSize)); | |
65 | + | |
66 | + ctx->YSum = 0; | |
67 | + ctx->Y2Sum = 0; | |
68 | + ctx->YRecords = calloc(sizeof(*ctx->YRecords), ctx->RecordLength); | |
69 | + ctx->Y2Records = calloc(sizeof (*ctx->Y2Records), ctx->RecordLength); | |
70 | + | |
71 | + ctx->XYSums = calloc(sizeof(*ctx->XYSums), ctx->TailLength); | |
72 | + ctx->XYRecords = calloc(sizeof(*ctx->XYRecords), ctx->RecordLength * ctx->TailLength); | |
73 | + | |
74 | + ctx->LastX = 0; | |
75 | + ctx->LastY = 0; | |
76 | + ctx->Weight = 1.0f / (ctx->RecordLength * ctx->WindowSize); | |
77 | + | |
78 | + return 0; | |
79 | +} | |
80 | + | |
81 | +void echocancel_end(struct echocancel_ctx* ctx) | |
82 | +{ | |
83 | + free(ctx->Xs); | |
84 | + free(ctx->XSums); | |
85 | + free(ctx->X2Sums); | |
86 | + free(ctx->XRecords); | |
87 | + free(ctx->YRecords); | |
88 | + free(ctx->Y2Records); | |
89 | + free(ctx->XYSums); | |
90 | + free(ctx->XYRecords); | |
91 | + memset(ctx, 0, sizeof(*ctx)); | |
92 | +} | |
93 | + | |
94 | +void echocancel_run(struct echocancel_ctx* ctx,int16_t *playbacked, int16_t *recorded) | |
95 | +{ | |
96 | + uint16_t *xRecords; | |
97 | + uint16_t ys[ctx->WindowSize]; | |
98 | + uint32_t ySum = 0; | |
99 | + uint32_t y2Sum = 0; | |
100 | + uint32_t *xyRecords; | |
101 | + int latency = 0; | |
102 | + float corr2 = 0.0f; | |
103 | + float varX = 0.0f; | |
104 | + float varY; | |
105 | + int i,j; | |
106 | + | |
107 | + // Update Xs. | |
108 | + for (i = ctx->TailLength - 1; i >= 0; --i) { | |
109 | + ctx->Xs[i + ctx->WindowSize] = ctx->Xs[i]; | |
110 | + } | |
111 | + for (i = ctx->WindowSize - 1, j = 0; i >= 0; --i, j += ctx->Scale) { | |
112 | + uint32_t sum = 0; | |
113 | + int k; | |
114 | + for (k = 0; k < ctx->Scale; ++k) { | |
115 | + int32_t x = playbacked[j + k] << 15; | |
116 | + ctx->LastX += x; | |
117 | + sum += ((ctx->LastX >= 0) ? ctx->LastX : -ctx->LastX) >> 15; | |
118 | + ctx->LastX -= (ctx->LastX >> 10) + x; | |
119 | + } | |
120 | + ctx->Xs[i] = sum >> ctx->Shift; | |
121 | + } | |
122 | + | |
123 | + // Update XSums, X2Sums, and XRecords. | |
124 | + for (i = ctx->TailLength - ctx->WindowSize - 1; i >= 0; --i) { | |
125 | + ctx->XSums[i + ctx->WindowSize] = ctx->XSums[i]; | |
126 | + ctx->X2Sums[i + ctx->WindowSize] = ctx->X2Sums[i]; | |
127 | + } | |
128 | + xRecords = &ctx->XRecords[ctx->RecordOffset * ctx->WindowSize]; | |
129 | + for (i = ctx->WindowSize - 1; i >= 0; --i) { | |
130 | + uint16_t x = ctx->Xs[i]; | |
131 | + ctx->XSums[i] = ctx->XSums[i + 1] + x - xRecords[i]; | |
132 | + ctx->X2Sums[i] = ctx->X2Sums[i + 1] + x * x - xRecords[i] * xRecords[i]; | |
133 | + xRecords[i] = x; | |
134 | + } | |
135 | + | |
136 | + // Compute Ys. | |
137 | + for (i = ctx->WindowSize - 1, j = 0; i >= 0; --i, j += ctx->Scale) { | |
138 | + uint32_t sum = 0; | |
139 | + int k; | |
140 | + for (k = 0; k < ctx->Scale; ++k) { | |
141 | + int32_t y = recorded[j + k] << 15; | |
142 | + ctx->LastY += y; | |
143 | + sum += ((ctx->LastY >= 0) ? ctx->LastY : -ctx->LastY) >> 15; | |
144 | + ctx->LastY -= (ctx->LastY >> 10) + y; | |
145 | + } | |
146 | + ys[i] = sum >> ctx->Shift; | |
147 | + } | |
148 | + | |
149 | + // Update YSum, Y2Sum, YRecords, and Y2Records. | |
150 | + ySum = 0; | |
151 | + y2Sum = 0; | |
152 | + for (i = ctx->WindowSize - 1; i >= 0; --i) { | |
153 | + ySum += ys[i]; | |
154 | + y2Sum += ys[i] * ys[i]; | |
155 | + } | |
156 | + ctx->YSum += ySum - ctx->YRecords[ctx->RecordOffset]; | |
157 | + ctx->Y2Sum += y2Sum - ctx->Y2Records[ctx->RecordOffset]; | |
158 | + ctx->YRecords[ctx->RecordOffset] = ySum; | |
159 | + ctx->Y2Records[ctx->RecordOffset] = y2Sum; | |
160 | + | |
161 | + // Update XYSums and XYRecords. | |
162 | + xyRecords = &ctx->XYRecords[ctx->RecordOffset * ctx->TailLength]; | |
163 | + for (i = ctx->TailLength - 1; i >= 0; --i) { | |
164 | + uint32_t xySum = 0; | |
165 | + for (j = ctx->WindowSize - 1; j >= 0; --j) { | |
166 | + xySum += ctx->Xs[i + j] * ys[j]; | |
167 | + } | |
168 | + ctx->XYSums[i] += xySum - xyRecords[i]; | |
169 | + xyRecords[i] = xySum; | |
170 | + } | |
171 | + | |
172 | + // Compute correlations. | |
173 | + latency = 0; | |
174 | + corr2 = 0.0f; | |
175 | + varX = 0.0f; | |
176 | + varY = ctx->Y2Sum - ctx->Weight * ctx->YSum * ctx->YSum; | |
177 | + for (i = ctx->TailLength - 1; i >= 0; --i) { | |
178 | + float cov = ctx->XYSums[i] - ctx->Weight * ctx->XSums[i] * ctx->YSum; | |
179 | + if (cov > 0.0f) { | |
180 | + float varXi = ctx->X2Sums[i] - ctx->Weight * ctx->XSums[i] * ctx->XSums[i]; | |
181 | + float corr2i = cov * cov / (varXi * varY + 1); | |
182 | + if (corr2i > corr2) { | |
183 | + varX = varXi; | |
184 | + corr2 = corr2i; | |
185 | + latency = i; | |
186 | + } | |
187 | + } | |
188 | + } | |
189 | + //ALOGI("corr^2 %.5f, var %8.0f %8.0f, latency %d", corr2, varX, varY, | |
190 | + // latency * mScale); | |
191 | + | |
192 | + // Do echo suppression. | |
193 | + if (corr2 > 0.1f && varX > 10000.0f) { | |
194 | + int factor = (corr2 > 1.0f) ? 0 : (1.0f - sqrtf(corr2)) * 4096; | |
195 | + for (i = 0; i < ctx->SampleCount; ++i) { | |
196 | + recorded[i] = recorded[i] * factor >> 16; | |
197 | + } | |
198 | + } | |
199 | + | |
200 | + // Increase RecordOffset. | |
201 | + ++ctx->RecordOffset; | |
202 | + if (ctx->RecordOffset == ctx->RecordLength) { | |
203 | + ctx->RecordOffset = 0; | |
204 | + } | |
205 | +} |
@@ -0,0 +1,63 @@ | ||
1 | +/* | |
2 | + * Copyrightm (C) 2010 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#ifndef __ECHO_CANCEL_H | |
18 | +#define __ECHO_CANCEL_H | |
19 | + | |
20 | +#include <stdint.h> | |
21 | + | |
22 | +struct echocancel_ctx { | |
23 | + int Shift; | |
24 | + int Scale; | |
25 | + int SampleCount; | |
26 | + int WindowSize; | |
27 | + int TailLength; | |
28 | + int RecordLength; | |
29 | + int RecordOffset; | |
30 | + | |
31 | + uint16_t *Xs; | |
32 | + uint32_t *XSums; | |
33 | + uint32_t *X2Sums; | |
34 | + uint16_t *XRecords; | |
35 | + | |
36 | + uint32_t YSum; | |
37 | + uint32_t Y2Sum; | |
38 | + uint32_t *YRecords; | |
39 | + uint32_t *Y2Records; | |
40 | + | |
41 | + uint32_t *XYSums; | |
42 | + uint32_t *XYRecords; | |
43 | + | |
44 | + int32_t LastX; | |
45 | + int32_t LastY; | |
46 | + | |
47 | + float Weight; | |
48 | +}; | |
49 | + | |
50 | +#ifdef __cplusplus | |
51 | +extern "C" { | |
52 | +#endif | |
53 | + | |
54 | +// The sampleCount must be power of 2. | |
55 | +int echocancel_init(struct echocancel_ctx* ctx,int sampleCount, int tailLength); | |
56 | +void echocancel_end(struct echocancel_ctx* ctx); | |
57 | +void echocancel_run(struct echocancel_ctx* ctx,int16_t *playbacked, int16_t *recorded); | |
58 | + | |
59 | +#ifdef __cplusplus | |
60 | +} | |
61 | +#endif | |
62 | + | |
63 | +#endif |
@@ -124,6 +124,6 @@ int fcp_to_ts_51011(/*in*/ const char *stream, /*in*/ size_t len, | ||
124 | 124 | |
125 | 125 | except: |
126 | 126 | #undef FCP_CVT_THROW |
127 | - ALOGE("%s() FCP to TS 510 11: Specification violation: %s.", __func__, what); | |
127 | + ALOGW("%s() FCP to TS 510 11: Specification violation: %s.", __func__, what); | |
128 | 128 | goto finally; |
129 | 129 | } |
@@ -171,7 +171,7 @@ gsm_hex_to_bytes( cbytes_t hex, int hexlen, bytes_t dst ) | ||
171 | 171 | int nn; |
172 | 172 | |
173 | 173 | for (nn = 0; nn < hexlen/2; nn++ ) { |
174 | - dst[nn] = (byte_t) gsm_hex2_to_byte0( hex+2*nn ); | |
174 | + dst[nn] = (byte_t) gsm_hex2_to_byte0( (char const*) hex+2*nn ); | |
175 | 175 | } |
176 | 176 | if (hexlen & 1) { |
177 | 177 | dst[nn] = gsm_hexchar_to_int0( hex[2*nn] ) << 4; |
@@ -282,7 +282,8 @@ gsm_rope_can_grow( GsmRope rope, int count ) | ||
282 | 282 | if (!rope->data || rope->error) |
283 | 283 | return 0; |
284 | 284 | |
285 | - if (rope->pos + count > rope->max) { | |
285 | + if (rope->pos + count > rope->max) | |
286 | + { | |
286 | 287 | if (rope->data == NULL) |
287 | 288 | rope->max = rope->pos + count; |
288 | 289 |
@@ -316,7 +317,8 @@ gsm_rope_reserve( GsmRope rope, int count ) | ||
316 | 317 | { |
317 | 318 | void* result = NULL; |
318 | 319 | |
319 | - if (gsm_rope_can_grow(rope, count)) { | |
320 | + if (gsm_rope_can_grow(rope, count)) | |
321 | + { | |
320 | 322 | if (rope->data != NULL) |
321 | 323 | result = rope->data + rope->pos; |
322 | 324 | } |
@@ -1100,7 +1102,7 @@ sim_adn_record_from_bytes( SimAdnRecord rec, cbytes_t data, int len ) | ||
1100 | 1102 | /* alpha is optional */ |
1101 | 1103 | if (len > ADN_FOOTER_SIZE) { |
1102 | 1104 | cbytes_t dataend = data + len - ADN_FOOTER_SIZE; |
1103 | - int count = sim_adn_alpha_to_utf8(data, dataend, NULL); | |
1105 | + unsigned int count = sim_adn_alpha_to_utf8(data, dataend, NULL); | |
1104 | 1106 | |
1105 | 1107 | if (count > sizeof(rec->adn.alpha)-1) /* too long */ |
1106 | 1108 | return -1; |
@@ -1116,7 +1118,7 @@ sim_adn_record_from_bytes( SimAdnRecord rec, cbytes_t data, int len ) | ||
1116 | 1118 | /* decode TON and number to ASCII, NOTE: this is lossy !! */ |
1117 | 1119 | { |
1118 | 1120 | int ton = footer[ADN_OFFSET_TON_NPI]; |
1119 | - bytes_t number = rec->adn.number; | |
1121 | + bytes_t number = (bytes_t) rec->adn.number; | |
1120 | 1122 | int len = sizeof(rec->adn.number)-1; |
1121 | 1123 | int count; |
1122 | 1124 |
@@ -1141,13 +1143,13 @@ sim_adn_record_to_bytes( SimAdnRecord rec, bytes_t data, int datalen ) | ||
1141 | 1143 | bytes_t end = data + datalen; |
1142 | 1144 | bytes_t footer = end - ADN_FOOTER_SIZE; |
1143 | 1145 | int ton = 0x81; |
1144 | - cbytes_t number = rec->adn.number; | |
1146 | + cbytes_t number = (cbytes_t) rec->adn.number; | |
1145 | 1147 | int len; |
1146 | 1148 | |
1147 | 1149 | if (number[0] == '+') { |
1148 | 1150 | ton = 0x91; |
1149 | 1151 | number += 1; |
1150 | 1152 | } |
1151 | - footer[0] = (strlen(number)+1)/2 + 1; | |
1153 | + footer[0] = (strlen((const char*) number)+1)/2 + 1; | |
1152 | 1154 | return 0; |
1153 | 1155 | } |
@@ -1,4 +1,4 @@ | ||
1 | -/* //device/system/htcgeneric-ril/htcgeneric-ril.c | |
1 | +/* | |
2 | 2 | ** |
3 | 3 | ** Copyright 2006, The Android Open Source Project |
4 | 4 | ** Copyright 2012 Eduardo Jos[e Tagle <ejtagle@tutopia.com> |
@@ -16,38 +16,10 @@ | ||
16 | 16 | ** limitations under the License. |
17 | 17 | */ |
18 | 18 | |
19 | -/* | |
20 | -AT^CURC=0 | |
21 | -ATQ0V1 | |
22 | - | |
23 | -^MODE: | |
24 | -^RSSI: | |
25 | -^SRVST: | |
26 | -+CCCM: | |
27 | -^CONN: | |
28 | -^CEND: | |
29 | -+CDSI: | |
30 | -^SMMEMFULL: | |
31 | -+CSSU: | |
32 | -^DCONN | |
33 | -^DEND | |
34 | -^SIMST ¡ | |
35 | -+CRING: | |
36 | -AT^SYSCFG=2,3,%s,1,2 | |
37 | -^RSSI | |
38 | -^SMMEMFULL | |
39 | -*/ | |
40 | - | |
41 | -/* | |
42 | -AT^SYSCFG=2,1,3FFFFFFF,1,2 for GPRS/EDGE Preferred | |
43 | -AT^SYSCFG=2,2,3FFFFFFF,1,2 for 3G Preferred | |
44 | -AT^SYSCFG=13,1,3FFFFFFF,1,2 for GPRS/EDGE Only | |
45 | -AT^SYSCFG=14,2,3FFFFFFF,1,2 for 3G Only | |
46 | - | |
47 | -The third parameter, 0x3FFFFFFF tells the card to use all bands. A value of 0x400380 here means GSM900/1800/WCDMA2100 only and a value of 0x200000 here means GSM1900 only. | |
19 | +//#define BYPASS_AUDIO_CHECK 1 | |
48 | 20 | |
49 | -I don't know what the final "1,2" parameter is for. But for some it has to be "2,4" instead for some reason. | |
50 | 21 | |
22 | +/* | |
51 | 23 | The status updates from the second port are prefixed with a caret and are of these forms: |
52 | 24 | |
53 | 25 | ^MODE:3,2 indicates GPRS |
@@ -92,6 +64,23 @@ then I plug in on my Linux Ubuntu computer, use minicom and type AT^U2DIAG=0 to | ||
92 | 64 | Maybe it's better to first type AT^U2DIAG=0 and then AT^U2DIAG=5 ... |
93 | 65 | |
94 | 66 | Regards, Phil. |
67 | + | |
68 | + | |
69 | +----- | |
70 | +1)with unaccepted sim | |
71 | +-> AT^CARDLOCK? | |
72 | +<-^CARDLOCK: 1,10,0 ............ locked | |
73 | +<-^CARDLOCK: 2,10,0 ............ unlocked | |
74 | +Quote: | |
75 | +Originally Posted by sergeymkl | |
76 | +4. second number of reply to CARDLOCK inquiry. | |
77 | +<-^CARDLOCK: 1,10,0 ............ locked | |
78 | +<-^CARDLOCK: 2,10,0 ............ unlocked | |
79 | +<-^CARDLOCK: A,B,C ............ | |
80 | +A = is to verify if locked or unlocked | |
81 | +B = attempts left? or total given attempts? | |
82 | +C = is for? | |
83 | + | |
95 | 84 | */ |
96 | 85 | |
97 | 86 | #define LOG_NDEBUG 0 |
@@ -112,6 +101,7 @@ Regards, Phil. | ||
112 | 101 | #include "atchannel.h" |
113 | 102 | #include "at_tok.h" |
114 | 103 | #include "misc.h" |
104 | +#include "net-utils.h" | |
115 | 105 | #include "gsm.h" |
116 | 106 | #include "requestdatahandler.h" |
117 | 107 | #include "fcp_parser.h" |
@@ -121,11 +111,18 @@ Regards, Phil. | ||
121 | 111 | #include <cutils/sockets.h> |
122 | 112 | #include <termios.h> |
123 | 113 | #include <cutils/properties.h> |
114 | +#include <log/logger.h> | |
124 | 115 | #include <stdbool.h> |
116 | +#include <stddef.h> | |
125 | 117 | |
126 | -#define D ALOGD | |
118 | +#include <linux/if.h> | |
119 | +#include <linux/sockios.h> | |
120 | +#include <linux/route.h> | |
121 | +#include <linux/wireless.h> | |
127 | 122 | |
128 | -#define RIL_VERSION_STRING "Huawei-ril 1.0.0.0" | |
123 | +#define D ALOGI | |
124 | + | |
125 | +#define RIL_VERSION_STRING "Huawei-ril 1.0.0.8 (Built on " __DATE__" " __TIME__ ")" | |
129 | 126 | |
130 | 127 | #define timespec_cmp(a, b, op) \ |
131 | 128 | ((a).tv_sec == (b).tv_sec \ |
@@ -136,10 +133,6 @@ Regards, Phil. | ||
136 | 133 | #define TIMEOUT_EMRDY 10 /* Module should respond at least within 10s */ |
137 | 134 | #define MAX_BUF 1024 |
138 | 135 | |
139 | -/* pathname returned from RIL_REQUEST_SETUP_DATA_CALL / RIL_REQUEST_SETUP_DEFAULT_PDP */ | |
140 | -#define PPP_TTY_PATH "ppp0" | |
141 | - | |
142 | - | |
143 | 136 | /** declarations */ |
144 | 137 | static const char *getVersion(); |
145 | 138 | static void signalCloseQueues(void); |
@@ -154,8 +147,9 @@ static int getCardStatus(RIL_CardStatus_v6 **pp_card_status); | ||
154 | 147 | static void freeCardStatus(RIL_CardStatus_v6 *p_card_status); |
155 | 148 | static void onDataCallListChanged(void *param); |
156 | 149 | static int killConn(const char * cid); |
157 | -static int wait_for_property(const char *name, const char *desired_value, int maxwait); | |
150 | +static int wait_for_property(const char *name, const char *desired_value, int maxwait, int allowempty); | |
158 | 151 | static void checkMessageStorageReady(void *p); |
152 | +static int pppSupported(void); | |
159 | 153 | static void onSIMReady(void *p); |
160 | 154 | static void pollSIMState(void *param); |
161 | 155 | static void checkMessageStorageReady(void *p); |
@@ -172,6 +166,19 @@ static const RIL_RadioFunctions s_callbacks = { | ||
172 | 166 | getVersion |
173 | 167 | }; |
174 | 168 | |
169 | +static char* rndis_iface_dev = "/dev/ttyUSB0"; | |
170 | +static char* ppp_iface_dev = "/dev/ttyUSB0"; | |
171 | +static int using_rndis = 0; | |
172 | + | |
173 | +/* Last call index status */ | |
174 | +static int lastCallIdx = 0; | |
175 | + | |
176 | +/* Last call fail cause */ | |
177 | +static int lastCallFailCause = CALL_FAIL_NORMAL; | |
178 | + | |
179 | +#define RNDIS_IFACE "rmnet0" | |
180 | +#define PPP_IFACE "ppp0" | |
181 | + | |
175 | 182 | static RIL_RadioState sState = RADIO_STATE_UNAVAILABLE; |
176 | 183 | static pthread_mutex_t s_state_mutex = PTHREAD_MUTEX_INITIALIZER; |
177 | 184 | static const struct timespec TIMEVAL_SIMPOLL = {1,0}; |
@@ -223,12 +230,15 @@ static RequestQueue *s_requestQueues[] = { | ||
223 | 230 | }; |
224 | 231 | |
225 | 232 | /* The Audio channel */ |
226 | -static struct GsmAudioTunnel sAudioChannel = GSM_AUDIO_CHANNEL_STATIC_INIT; | |
233 | +static struct GsmAudioTunnel sAudioChannel; | |
227 | 234 | static char sAudioDevice[64] = {0}; |
228 | 235 | |
236 | +static int audioTunnelMuted = 0; // If audio tunnel is muted | |
237 | + | |
229 | 238 | /* Starts the audio tunnel channel */ |
230 | -static void startAudioTunnel(void) | |
239 | +static void startAudioTunnel(void* param) | |
231 | 240 | { |
241 | + ALOGD("call startAudioTunnel"); | |
232 | 242 | #if 0 |
233 | 243 | ATResponse *atResponse = NULL; |
234 | 244 | int err; |
@@ -236,16 +246,16 @@ static void startAudioTunnel(void) | ||
236 | 246 | int voice_disabled = 0, sampling_rate = 8000, |
237 | 247 | bits_per_sample = 16, framesize_ms = 20; |
238 | 248 | |
239 | - if (sAudioChannel.running) | |
249 | + if (gsm_audio_tunnel_running(&sAudioChannel)) | |
240 | 250 | return; |
241 | 251 | |
242 | - // Enable voice function | |
252 | + // Enable voice function - Some firmwares fail this command, but | |
253 | + // nevertheless, it works. Leave validation for the next one. | |
243 | 254 | err = at_send_command("AT^CVOICE=0"); |
244 | - if (err != AT_NOERROR) | |
245 | - goto error; | |
246 | 255 | |
247 | - // Check status. | |
248 | - err = at_send_command_singleline("AT^CVOICE=?", "^CVOICE:", &atResponse); | |
256 | +#ifndef BYPASS_AUDIO_CHECK | |
257 | + // Check status - This is the check that matters! | |
258 | + err = at_send_command_singleline("AT^CVOICE?", "^CVOICE:", &atResponse); | |
249 | 259 | if (err != AT_NOERROR) |
250 | 260 | goto error; |
251 | 261 |
@@ -256,6 +266,9 @@ static void startAudioTunnel(void) | ||
256 | 266 | err = at_tok_nextint(&line, &voice_disabled); |
257 | 267 | if (err < 0) goto error; |
258 | 268 | |
269 | + // Check if voice was really enabled | |
270 | + if (voice_disabled != 0) goto error; | |
271 | + | |
259 | 272 | err = at_tok_nextint(&line, &sampling_rate); |
260 | 273 | if (err < 0) goto error; |
261 | 274 |
@@ -264,11 +277,17 @@ static void startAudioTunnel(void) | ||
264 | 277 | |
265 | 278 | err = at_tok_nextint(&line, &framesize_ms); |
266 | 279 | if (err < 0) goto error; |
280 | +#else | |
281 | + sampling_rate = 8000; | |
282 | + bits_per_sample = 16; | |
283 | + framesize_ms = 20; | |
284 | +#endif | |
267 | 285 | |
268 | - /* Switch to audio from USB1 */ | |
269 | - err = at_send_command("AT^DDSETEX=2"); | |
270 | - if (err != AT_NOERROR) | |
271 | - goto error; | |
286 | + /* Increase volume to maximum. Some modems do not set the volume if not | |
287 | + actually changing values.... And some of them do not understand the | |
288 | + command at all. Try to switch volume to maximum - DerArtem */ | |
289 | + err = at_send_command("AT+CLVL=1"); /* Lo volume */ | |
290 | + err = at_send_command("AT+CLVL=5"); /* Maximum volume */ | |
272 | 291 | |
273 | 292 | /* Start the audio channel */ |
274 | 293 | err = gsm_audio_tunnel_start(&sAudioChannel,sAudioDevice, |
@@ -276,7 +295,10 @@ static void startAudioTunnel(void) | ||
276 | 295 | if (err) |
277 | 296 | goto error; |
278 | 297 | |
279 | - ALOGE("Audio Tunnel enabled"); | |
298 | + // Mute/Ummute audioTunnel as requested | |
299 | + gsm_audio_tunnel_mute(&sAudioChannel,audioTunnelMuted); | |
300 | + | |
301 | + ALOGD("Audio Tunnel enabled"); | |
280 | 302 | at_response_free(atResponse); |
281 | 303 | return; |
282 | 304 |
@@ -284,21 +306,37 @@ error: | ||
284 | 306 | ALOGE("Failed to start audio tunnel"); |
285 | 307 | |
286 | 308 | at_response_free(atResponse); |
287 | - return; | |
288 | 309 | #endif |
310 | + return; | |
289 | 311 | } |
290 | 312 | |
291 | 313 | /* Stops the audio tunnel channel */ |
292 | -static void stopAudioTunnel(void) | |
314 | +static void stopAudioTunnel(void* param) | |
293 | 315 | { |
294 | 316 | #if 0 |
295 | - if (!sAudioChannel.running) | |
317 | + if (!gsm_audio_tunnel_running(&sAudioChannel)) | |
296 | 318 | return; |
297 | 319 | |
298 | 320 | // Stop the audio tunnel |
299 | 321 | gsm_audio_tunnel_stop(&sAudioChannel); |
322 | +#endif | |
323 | + | |
324 | + ALOGD("Audio Tunnel disabled"); | |
325 | +} | |
326 | + | |
327 | +static void muteAudioTunnel(int muteit) | |
328 | +{ | |
329 | + ALOGD("call muteAudioTunnel"); | |
330 | +#if 0 | |
331 | + audioTunnelMuted = muteit; | |
300 | 332 | |
301 | - D("Audio Tunnel disabled"); | |
333 | + ALOGD("Audio Tunnel %smuted",muteit?"":"un"); | |
334 | + | |
335 | + if (!gsm_audio_tunnel_running(&sAudioChannel)) | |
336 | + return; | |
337 | + | |
338 | + // Mute/Ummute audioTunnel if requested | |
339 | + gsm_audio_tunnel_mute(&sAudioChannel,audioTunnelMuted); | |
302 | 340 | #endif |
303 | 341 | } |
304 | 342 |
@@ -313,15 +351,14 @@ static void enqueueRILEvent(void (*callback) (void *param), | ||
313 | 351 | char done = 0; |
314 | 352 | RequestQueue *q = NULL; |
315 | 353 | |
316 | - RILEvent *e = (RILEvent *) malloc(sizeof(RILEvent)); | |
317 | - memset(e, 0, sizeof(RILEvent)); | |
354 | + RILEvent *e = (RILEvent *) calloc(1, sizeof(RILEvent)); | |
318 | 355 | |
319 | 356 | e->eventCallback = callback; |
320 | 357 | e->param = param; |
321 | 358 | |
322 | 359 | if (relativeTime == NULL) { |
323 | 360 | relativeTime = (const struct timespec *) alloca(sizeof(struct timespec)); |
324 | - memset((struct timespec *) relativeTime, 0, sizeof(struct timespec)); | |
361 | + memset((void *) relativeTime, 0, sizeof(struct timespec)); | |
325 | 362 | } |
326 | 363 | |
327 | 364 | clock_gettime(CLOCK_MONOTONIC, &ts); |
@@ -376,16 +413,16 @@ again: | ||
376 | 413 | |
377 | 414 | } |
378 | 415 | |
416 | + | |
379 | 417 | /** returns 1 if on, 0 if off, and -1 on error */ |
380 | 418 | static int isRadioOn() |
381 | 419 | { |
382 | 420 | ATResponse *atResponse = NULL; |
383 | - int err,ison; | |
421 | + int err; | |
384 | 422 | char *line; |
385 | - char ret1,ret2; | |
386 | - | |
387 | - err = at_send_command_singleline("AT^RFSWITCH?", "^RFSWITCH:", &atResponse); | |
423 | + char ret1; | |
388 | 424 | |
425 | + err = at_send_command_singleline("AT+CFUN?", "+CFUN:", &atResponse); | |
389 | 426 | if (err != AT_NOERROR) { |
390 | 427 | // assume radio is off |
391 | 428 | goto error; |
@@ -399,20 +436,14 @@ static int isRadioOn() | ||
399 | 436 | err = at_tok_nextbool(&line, &ret1); |
400 | 437 | if (err < 0) goto error; |
401 | 438 | |
402 | - err = at_tok_nextbool(&line, &ret2); | |
403 | - if (err < 0) goto error; | |
404 | - | |
405 | 439 | at_response_free(atResponse); |
406 | 440 | |
407 | - /* 1 is ON, 0 is OFF */ | |
408 | - ison = (ret1 == 1 && ret2 == 1); | |
441 | + ALOGD("Radio is %s", (ret1 == 1) ? "On" : "Off"); | |
409 | 442 | |
410 | - ALOGD("Radio is %s", ison ? "On" : "Off"); | |
411 | - | |
412 | - return ison; | |
443 | + /* 1=means online, all the others, mean offline */ | |
444 | + return (ret1 == 1) ? 1 : 0; | |
413 | 445 | |
414 | 446 | error: |
415 | - | |
416 | 447 | at_response_free(atResponse); |
417 | 448 | return -1; |
418 | 449 | } |
@@ -423,39 +454,39 @@ static const char *radioStateToString(RIL_RadioState radioState) | ||
423 | 454 | const char *state; |
424 | 455 | |
425 | 456 | switch (radioState) { |
426 | - case RADIO_STATE_OFF: | |
427 | - state = "RADIO_STATE_OFF"; | |
428 | - break; | |
429 | - case RADIO_STATE_UNAVAILABLE: | |
430 | - state = "RADIO_STATE_UNAVAILABLE"; | |
431 | - break; | |
432 | - case RADIO_STATE_SIM_NOT_READY: | |
433 | - state = "RADIO_STATE_SIM_NOT_READY"; | |
434 | - break; | |
435 | - case RADIO_STATE_SIM_LOCKED_OR_ABSENT: | |
436 | - state = "RADIO_STATE_SIM_LOCKED_OR_ABSENT"; | |
437 | - break; | |
438 | - case RADIO_STATE_SIM_READY: | |
439 | - state = "RADIO_STATE_SIM_READY"; | |
440 | - break; | |
441 | - case RADIO_STATE_RUIM_NOT_READY: | |
442 | - state = "RADIO_STATE_RUIM_NOT_READY"; | |
443 | - break; | |
444 | - case RADIO_STATE_RUIM_READY: | |
445 | - state = "RADIO_STATE_RUIM_READY"; | |
446 | - break; | |
447 | - case RADIO_STATE_RUIM_LOCKED_OR_ABSENT: | |
448 | - state = "RADIO_STATE_RUIM_READY"; | |
449 | - break; | |
450 | - case RADIO_STATE_NV_NOT_READY: | |
451 | - state = "RADIO_STATE_NV_NOT_READY"; | |
452 | - break; | |
453 | - case RADIO_STATE_NV_READY: | |
454 | - state = "RADIO_STATE_NV_READY"; | |
455 | - break; | |
456 | - default: | |
457 | - state = "RADIO_STATE_<> Unknown!"; | |
458 | - break; | |
457 | + case RADIO_STATE_OFF: | |
458 | + state = "RADIO_STATE_OFF"; | |
459 | + break; | |
460 | + case RADIO_STATE_UNAVAILABLE: | |
461 | + state = "RADIO_STATE_UNAVAILABLE"; | |
462 | + break; | |
463 | + case RADIO_STATE_SIM_NOT_READY: | |
464 | + state = "RADIO_STATE_SIM_NOT_READY"; | |
465 | + break; | |
466 | + case RADIO_STATE_SIM_LOCKED_OR_ABSENT: | |
467 | + state = "RADIO_STATE_SIM_LOCKED_OR_ABSENT"; | |
468 | + break; | |
469 | + case RADIO_STATE_SIM_READY: | |
470 | + state = "RADIO_STATE_SIM_READY"; | |
471 | + break; | |
472 | + case RADIO_STATE_RUIM_NOT_READY: | |
473 | + state = "RADIO_STATE_RUIM_NOT_READY"; | |
474 | + break; | |
475 | + case RADIO_STATE_RUIM_READY: | |
476 | + state = "RADIO_STATE_RUIM_READY"; | |
477 | + break; | |
478 | + case RADIO_STATE_RUIM_LOCKED_OR_ABSENT: | |
479 | + state = "RADIO_STATE_RUIM_READY"; | |
480 | + break; | |
481 | + case RADIO_STATE_NV_NOT_READY: | |
482 | + state = "RADIO_STATE_NV_NOT_READY"; | |
483 | + break; | |
484 | + case RADIO_STATE_NV_READY: | |
485 | + state = "RADIO_STATE_NV_READY"; | |
486 | + break; | |
487 | + default: | |
488 | + state = "RADIO_STATE_<> Unknown!"; | |
489 | + break; | |
459 | 490 | } |
460 | 491 | |
461 | 492 | return state; |
@@ -471,7 +502,6 @@ static RIL_RadioState getRadioState() | ||
471 | 502 | return sState; |
472 | 503 | } |
473 | 504 | |
474 | - | |
475 | 505 | static void setRadioState(RIL_RadioState newState) |
476 | 506 | { |
477 | 507 | RIL_RadioState oldState; |
@@ -483,7 +513,7 @@ static void setRadioState(RIL_RadioState newState) | ||
483 | 513 | oldState = sState; |
484 | 514 | |
485 | 515 | ALOGI("%s() oldState=%s newState=%s", __func__, radioStateToString(oldState), |
486 | - radioStateToString(newState)); | |
516 | + radioStateToString(newState)); | |
487 | 517 | |
488 | 518 | sState = newState; |
489 | 519 |
@@ -493,16 +523,63 @@ static void setRadioState(RIL_RadioState newState) | ||
493 | 523 | /* Do these outside of the mutex. */ |
494 | 524 | if (sState != oldState || sState == RADIO_STATE_SIM_LOCKED_OR_ABSENT) { |
495 | 525 | RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, |
496 | - NULL, 0); | |
526 | + NULL, 0); | |
497 | 527 | |
498 | 528 | if (sState == RADIO_STATE_SIM_READY) { |
499 | 529 | enqueueRILEvent(checkMessageStorageReady, NULL, NULL); |
500 | 530 | enqueueRILEvent(onSIMReady, NULL, NULL); |
501 | - } else if (sState == RADIO_STATE_SIM_NOT_READY) | |
531 | + } else if (sState == RADIO_STATE_SIM_NOT_READY) { | |
502 | 532 | enqueueRILEvent(pollSIMState, NULL, NULL); |
533 | + } | |
534 | + } | |
535 | +} | |
536 | + | |
537 | +/* | |
538 | +-> AT^CARDLOCK? | |
539 | +<-^CARDLOCK: 1,10,0 ............ locked to an operator: Can be unlocked by code | |
540 | +<-^CARDLOCK: 2,10,0 ............ unlocked | |
541 | +<-^CARDLOCK: 3,10,0 ............ locked: Datacard is locked forever to an operator | |
542 | +<-^CARDLOCK: A,B,C ............ | |
543 | +A = is to verify if locked or unlocked | |
544 | +B = attempts left? or total given attempts? | |
545 | +C = is for? | |
546 | +*/ | |
547 | + | |
548 | +static int getSimLockStatus(int* status, int* times) | |
549 | +{ | |
550 | + ATResponse *atResponse = NULL; | |
551 | + int err; | |
552 | + char *line; | |
553 | + | |
554 | + err = at_send_command_singleline("AT^CARDLOCK?", "^CARDLOCK:", &atResponse); | |
555 | + | |
556 | + if (err != AT_NOERROR) { | |
557 | + // assume locked | |
558 | + goto error; | |
503 | 559 | } |
560 | + | |
561 | + line = atResponse->p_intermediates->line; | |
562 | + | |
563 | + err = at_tok_start(&line); | |
564 | + if (err < 0) goto error; | |
565 | + | |
566 | + err = at_tok_nextint(&line, status); | |
567 | + if (err < 0) goto error; | |
568 | + | |
569 | + err = at_tok_nextint(&line, times); | |
570 | + if (err < 0) goto error; | |
571 | + | |
572 | + at_response_free(atResponse); | |
573 | + | |
574 | + ALOGD("Card %s, times: %d", *status == 1 ? "Locked" : "Unlocked" , *times); | |
575 | + return 0; | |
576 | + | |
577 | +error: | |
578 | + at_response_free(atResponse); | |
579 | + return -1; | |
504 | 580 | } |
505 | 581 | |
582 | + | |
506 | 583 | typedef enum { |
507 | 584 | SIM_ABSENT = 0, /* SIM card is not inserted */ |
508 | 585 | SIM_NOT_READY = 1, /* SIM card is not ready */ |
@@ -534,6 +611,8 @@ typedef enum { | ||
534 | 611 | UICC_TYPE_UIM, |
535 | 612 | } UICC_Type; |
536 | 613 | |
614 | + | |
615 | + | |
537 | 616 | /** Returns one of SIM_*. Returns SIM_NOT_READY on error. */ |
538 | 617 | static SIM_Status getSIMStatus(void) |
539 | 618 | { |
@@ -542,6 +621,7 @@ static SIM_Status getSIMStatus(void) | ||
542 | 621 | SIM_Status ret = SIM_ABSENT; |
543 | 622 | char *cpinLine; |
544 | 623 | char *cpinResult; |
624 | + int status, times; | |
545 | 625 | |
546 | 626 | if (getRadioState() == RADIO_STATE_OFF || |
547 | 627 | getRadioState() == RADIO_STATE_UNAVAILABLE) { |
@@ -557,51 +637,51 @@ static SIM_Status getSIMStatus(void) | ||
557 | 637 | } |
558 | 638 | |
559 | 639 | switch (at_get_cme_error(err)) { |
560 | - case CME_SIM_NOT_INSERTED: | |
561 | - ret = SIM_ABSENT; | |
562 | - break; | |
563 | - case CME_SIM_PIN_REQUIRED: | |
564 | - ret = SIM_PIN; | |
565 | - break; | |
566 | - case CME_SIM_PUK_REQUIRED: | |
567 | - ret = SIM_PUK; | |
568 | - break; | |
569 | - case CME_SIM_PIN2_REQUIRED: | |
570 | - ret = SIM_PIN2; | |
571 | - break; | |
572 | - case CME_SIM_PUK2_REQUIRED: | |
573 | - ret = SIM_PUK2; | |
574 | - break; | |
575 | - case CME_NETWORK_PERSONALIZATION_PIN_REQUIRED: | |
576 | - ret = SIM_NETWORK_PERSO; | |
577 | - break; | |
578 | - case CME_NETWORK_PERSONALIZATION_PUK_REQUIRED: | |
579 | - ret = SIM_NETWORK_PERSO_PUK; | |
580 | - break; | |
581 | - case CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED: | |
582 | - ret = SIM_NETWORK_SUBSET_PERSO; | |
583 | - break; | |
584 | - case CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED: | |
585 | - ret = SIM_NETWORK_SUBSET_PERSO_PUK; | |
586 | - break; | |
587 | - case CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED: | |
588 | - ret = SIM_SERVICE_PROVIDER_PERSO; | |
589 | - break; | |
590 | - case CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED: | |
591 | - ret = SIM_SERVICE_PROVIDER_PERSO_PUK; | |
592 | - break; | |
593 | - case CME_PH_SIMLOCK_PIN_REQUIRED: /* PUK not in use by modem */ | |
594 | - ret = SIM_SIM_PERSO; | |
595 | - break; | |
596 | - case CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED: | |
597 | - ret = SIM_CORPORATE_PERSO; | |
598 | - break; | |
599 | - case CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED: | |
600 | - ret = SIM_CORPORATE_PERSO_PUK; | |
601 | - break; | |
602 | - default: | |
603 | - ret = SIM_NOT_READY; | |
604 | - break; | |
640 | + case CME_SIM_NOT_INSERTED: | |
641 | + ret = SIM_ABSENT; | |
642 | + break; | |
643 | + case CME_SIM_PIN_REQUIRED: | |
644 | + ret = SIM_PIN; | |
645 | + break; | |
646 | + case CME_SIM_PUK_REQUIRED: | |
647 | + ret = SIM_PUK; | |
648 | + break; | |
649 | + case CME_SIM_PIN2_REQUIRED: | |
650 | + ret = SIM_PIN2; | |
651 | + break; | |
652 | + case CME_SIM_PUK2_REQUIRED: | |
653 | + ret = SIM_PUK2; | |
654 | + break; | |
655 | + case CME_NETWORK_PERSONALIZATION_PIN_REQUIRED: | |
656 | + ret = SIM_NETWORK_PERSO; | |
657 | + break; | |
658 | + case CME_NETWORK_PERSONALIZATION_PUK_REQUIRED: | |
659 | + ret = SIM_NETWORK_PERSO_PUK; | |
660 | + break; | |
661 | + case CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED: | |
662 | + ret = SIM_NETWORK_SUBSET_PERSO; | |
663 | + break; | |
664 | + case CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED: | |
665 | + ret = SIM_NETWORK_SUBSET_PERSO_PUK; | |
666 | + break; | |
667 | + case CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED: | |
668 | + ret = SIM_SERVICE_PROVIDER_PERSO; | |
669 | + break; | |
670 | + case CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED: | |
671 | + ret = SIM_SERVICE_PROVIDER_PERSO_PUK; | |
672 | + break; | |
673 | + case CME_PH_SIMLOCK_PIN_REQUIRED: /* PUK not in use by modem */ | |
674 | + ret = SIM_SIM_PERSO; | |
675 | + break; | |
676 | + case CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED: | |
677 | + ret = SIM_CORPORATE_PERSO; | |
678 | + break; | |
679 | + case CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED: | |
680 | + ret = SIM_CORPORATE_PERSO_PUK; | |
681 | + break; | |
682 | + default: | |
683 | + ret = SIM_NOT_READY; | |
684 | + break; | |
605 | 685 | } |
606 | 686 | return ret; |
607 | 687 | } |
@@ -625,6 +705,11 @@ static SIM_Status getSIMStatus(void) | ||
625 | 705 | |
626 | 706 | if (0 == strcmp(cpinResult, "READY")) { |
627 | 707 | ret = SIM_READY; |
708 | + if (getSimLockStatus(&status,×) < 0) | |
709 | + goto done; | |
710 | + if (status == 1 || status == 3) { | |
711 | + ret = SIM_NETWORK_PERSO; | |
712 | + } | |
628 | 713 | } else if (0 == strcmp(cpinResult, "SIM PIN")) { |
629 | 714 | ret = SIM_PIN; |
630 | 715 | } else if (0 == strcmp(cpinResult, "SIM PUK")) { |
@@ -681,41 +766,39 @@ static void pollSIMState(void *param) | ||
681 | 766 | return; |
682 | 767 | |
683 | 768 | switch (getSIMStatus()) { |
684 | - case SIM_NOT_READY: | |
685 | - ALOGI("SIM_NOT_READY, poll for sim state."); | |
686 | - enqueueRILEvent(pollSIMState, NULL, &TIMEVAL_SIMPOLL); | |
687 | - return; | |
769 | + case SIM_NOT_READY: | |
770 | + ALOGI("SIM_NOT_READY, poll for sim state."); | |
771 | + enqueueRILEvent(pollSIMState, NULL, &TIMEVAL_SIMPOLL); | |
772 | + return; | |
688 | 773 | |
689 | - case SIM_PIN2: | |
690 | - case SIM_PUK2: | |
691 | - case SIM_PUK2_PERM_BLOCKED: | |
692 | - case SIM_READY: | |
693 | - setRadioState(RADIO_STATE_SIM_READY); | |
694 | - return; | |
695 | - case SIM_ABSENT: | |
696 | - case SIM_PIN: | |
697 | - case SIM_PUK: | |
698 | - case SIM_NETWORK_PERSO: | |
699 | - case SIM_NETWORK_SUBSET_PERSO: | |
700 | - case SIM_SERVICE_PROVIDER_PERSO: | |
701 | - case SIM_CORPORATE_PERSO: | |
702 | - case SIM_SIM_PERSO: | |
703 | - case SIM_STERICSSON_LOCK: | |
704 | - case SIM_BLOCKED: | |
705 | - case SIM_PERM_BLOCKED: | |
706 | - case SIM_NETWORK_PERSO_PUK: | |
707 | - case SIM_NETWORK_SUBSET_PERSO_PUK: | |
708 | - case SIM_SERVICE_PROVIDER_PERSO_PUK: | |
709 | - case SIM_CORPORATE_PERSO_PUK: | |
710 | - /* pass through, do not break */ | |
711 | - default: | |
712 | - setRadioState(RADIO_STATE_SIM_LOCKED_OR_ABSENT); | |
713 | - return; | |
774 | + case SIM_PIN2: | |
775 | + case SIM_PUK2: | |
776 | + case SIM_PUK2_PERM_BLOCKED: | |
777 | + case SIM_READY: | |
778 | + setRadioState(RADIO_STATE_SIM_READY); | |
779 | + return; | |
780 | + case SIM_ABSENT: | |
781 | + case SIM_PIN: | |
782 | + case SIM_PUK: | |
783 | + case SIM_NETWORK_PERSO: | |
784 | + case SIM_NETWORK_SUBSET_PERSO: | |
785 | + case SIM_SERVICE_PROVIDER_PERSO: | |
786 | + case SIM_CORPORATE_PERSO: | |
787 | + case SIM_SIM_PERSO: | |
788 | + case SIM_STERICSSON_LOCK: | |
789 | + case SIM_BLOCKED: | |
790 | + case SIM_PERM_BLOCKED: | |
791 | + case SIM_NETWORK_PERSO_PUK: | |
792 | + case SIM_NETWORK_SUBSET_PERSO_PUK: | |
793 | + case SIM_SERVICE_PROVIDER_PERSO_PUK: | |
794 | + case SIM_CORPORATE_PERSO_PUK: | |
795 | + /* pass through, do not break */ | |
796 | + default: | |
797 | + setRadioState(RADIO_STATE_SIM_LOCKED_OR_ABSENT); | |
798 | + return; | |
714 | 799 | } |
715 | 800 | } |
716 | 801 | |
717 | - | |
718 | - | |
719 | 802 | static void sendCallStateChanged(void *param) |
720 | 803 | { |
721 | 804 | RIL_onUnsolicitedResponse ( |
@@ -725,9 +808,8 @@ static void sendCallStateChanged(void *param) | ||
725 | 808 | |
726 | 809 | |
727 | 810 | static int clccStateToRILState(int state, RIL_CallState *p_state) |
728 | - | |
729 | 811 | { |
730 | - switch(state) { | |
812 | + switch (state) { | |
731 | 813 | case 0: *p_state = RIL_CALL_ACTIVE; return 0; |
732 | 814 | case 1: *p_state = RIL_CALL_HOLDING; return 0; |
733 | 815 | case 2: *p_state = RIL_CALL_DIALING; return 0; |
@@ -868,16 +950,20 @@ static void requestGetCurrentCalls(void *data, size_t datalen, RIL_Token t) | ||
868 | 950 | |
869 | 951 | ALOGI("Calls=%d,Valid=%d", countCalls, countValidCalls); |
870 | 952 | |
871 | - /* If some voice calls are active, enable audio tunnel */ | |
953 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, pp_calls, | |
954 | + countValidCalls * sizeof (RIL_Call *)); | |
955 | + | |
956 | + /* Start/stop audio tunnel if a valid voice call is detected - | |
957 | + This will restart the audiotunnel when we preventively shut it | |
958 | + down after user hangs call (but should be restarted if in conference | |
959 | + Stopping should be handled by the connection end notification, but, | |
960 | + just in case, we also handle it here*/ | |
872 | 961 | if (countValidCalls) { |
873 | - startAudioTunnel(); | |
962 | + startAudioTunnel(NULL); | |
874 | 963 | } else { |
875 | - stopAudioTunnel(); | |
964 | + stopAudioTunnel(NULL); | |
876 | 965 | } |
877 | 966 | |
878 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, pp_calls, | |
879 | - countValidCalls * sizeof (RIL_Call *)); | |
880 | - | |
881 | 967 | at_response_free(atResponse); |
882 | 968 | |
883 | 969 | if (needRepoll) { |
@@ -946,204 +1032,770 @@ error: | ||
946 | 1032 | |
947 | 1033 | |
948 | 1034 | /* wait for a propertyvalue change */ |
949 | -static int wait_for_property(const char *name, const char *desired_value, int maxwait) | |
1035 | +static int wait_for_property(const char *name, const char *desired_value, int maxwait, int allowempty) | |
950 | 1036 | { |
951 | 1037 | char value[PROPERTY_VALUE_MAX] = {'\0'}; |
952 | - int maxnaps = maxwait / 1; | |
1038 | + int maxnaps = maxwait; | |
953 | 1039 | |
954 | 1040 | if (maxnaps < 1) { |
955 | 1041 | maxnaps = 1; |
956 | 1042 | } |
957 | 1043 | |
958 | 1044 | while (maxnaps-- > 0) { |
959 | - usleep(1000000); | |
960 | 1045 | if (property_get(name, value, NULL)) { |
961 | - if (desired_value == NULL || | |
962 | - strcmp(value, desired_value) == 0) { | |
963 | - return 0; | |
1046 | + if (allowempty || (!allowempty && value[0]!=0)) { | |
1047 | + if (desired_value == NULL || | |
1048 | + strcmp(value, desired_value) == 0) { | |
1049 | + return 0; | |
1050 | + } | |
964 | 1051 | } |
965 | 1052 | } |
1053 | + sleep(1); | |
966 | 1054 | } |
967 | 1055 | return -1; /* failure */ |
968 | 1056 | } |
969 | 1057 | |
970 | -static int killConn(const char * cid) | |
1058 | +/* Write an string to the modem */ | |
1059 | +static int dial_at_modem(const char* cmd, int skipanswerwait) | |
971 | 1060 | { |
972 | - int err; | |
973 | - int fd; | |
974 | - int i=0; | |
1061 | + int i, n, ret; | |
1062 | + int fd = -1; | |
1063 | + char buf[64+1]; | |
1064 | + | |
1065 | + ALOGD("dial_at_modem: opening modem %s", ppp_iface_dev); | |
1066 | + do { | |
1067 | + fd = open( ppp_iface_dev, O_RDWR | O_NOCTTY | O_NDELAY); /* nonblocking */ | |
1068 | + } while (fd < 0 && errno == EINTR); | |
1069 | + | |
1070 | + if (fd < 0) { | |
1071 | + ALOGE("could not open modem %s: %s", ppp_iface_dev, strerror(errno) ); | |
1072 | + return -1; | |
1073 | + } | |
1074 | + | |
1075 | + // disable echo on serial lines and set a 10 second timeout | |
1076 | + if ( isatty( fd ) ) { | |
1077 | + struct termios ios; | |
1078 | + tcgetattr( fd, &ios ); | |
1079 | + ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */ | |
1080 | + ios.c_oflag &= (~ONLCR); /* Stop \n -> \r\n translation on output */ | |
1081 | + ios.c_iflag &= (~(ICRNL | INLCR)); /* Stop \r -> \n & \n -> \r translation on input */ | |
1082 | + ios.c_iflag |= (IGNCR | IXOFF); /* Ignore \r & XON/XOFF on input */ | |
1083 | + ios.c_cc[VTIME] = 10; /* Timeout in 1/10 of a second */ | |
1084 | + ios.c_cc[VMIN] = 0; /* Minimum number of chars returned: 0*/ | |
1085 | + tcsetattr( fd, TCSANOW, &ios ); | |
1086 | + } | |
1087 | + | |
1088 | + // Discard all pending data */ | |
1089 | + tcflush(fd, TCIOFLUSH); | |
1090 | + | |
1091 | + // If hangup sequence, wait a bit... | |
1092 | + if (!strcmp(cmd,"+++")) { | |
1093 | + sleep(1); | |
1094 | + } | |
1095 | + | |
1096 | + // Write command to modem | |
1097 | + ALOGD("dial_at_modem: writing command: %s", cmd); | |
1098 | + i = strlen(cmd); | |
1099 | + n = 0; | |
1100 | + do { | |
1101 | + ret = write(fd, cmd + n, i - n); | |
1102 | + if (ret < 0 && errno == EINTR) | |
1103 | + continue; | |
1104 | + if (ret < 0) { | |
1105 | + ALOGE("Error writing to modem: %s", strerror(errno) ); | |
1106 | + close(fd); | |
1107 | + return -1; | |
1108 | + } | |
1109 | + n += ret; | |
1110 | + } while (n < i); | |
975 | 1111 | |
976 | - D("killConn"); | |
1112 | + // Force all the data to be transmitted | |
1113 | + tcdrain(fd); | |
1114 | + | |
1115 | + // If hangup sequence, wait a bit... and we are done. Modem won't answer it | |
1116 | + if (!strcmp(cmd,"+++")) { | |
1117 | + sleep(1); | |
1118 | + | |
1119 | + // Leave line ready */ | |
1120 | + write(fd, "\r\n", 2); | |
1121 | + tcdrain(fd); | |
977 | 1122 | |
978 | -#if 0 | |
979 | - while ((fd = open("/sys/class/net/ppp0/ifindex",O_RDONLY)) > 0) | |
980 | - { | |
981 | - if(i%5 == 0) | |
982 | - system("killall pppd"); | |
983 | - if(i>25) | |
984 | - goto error; | |
985 | - i++; | |
986 | 1123 | close(fd); |
987 | - sleep(2); | |
988 | - } | |
989 | -#elif 0 | |
990 | - // Requires root access... | |
991 | - property_set("ctl.stop", "pppd_gprs"); | |
992 | - if (wait_for_property("init.svc.pppd_gprs", "stopped", 10) < 0) { | |
993 | - goto error; | |
1124 | + return 0; | |
994 | 1125 | } |
995 | -#endif | |
996 | 1126 | |
997 | - D("killall pppd finished"); | |
1127 | + // If we must skip waiting for an answer | |
1128 | + if (skipanswerwait) { | |
1129 | + close(fd); | |
1130 | + return 0; | |
1131 | + } | |
998 | 1132 | |
999 | - err = at_send_command("AT+CGACT=0,%s", cid); | |
1000 | - if (err != AT_NOERROR) | |
1001 | - goto error; | |
1133 | + // Read answer with timeout | |
1134 | + ALOGD("dial_at_modem: waiting for response"); | |
1135 | + do { | |
1002 | 1136 | |
1003 | - at_send_command("ATH"); | |
1004 | - return 0; | |
1137 | + /* Receive a line with timeout */ | |
1138 | + int waitc = 5; | |
1139 | + n = 0; | |
1140 | + do { | |
1141 | + ret = read(fd, &buf[n], 1); | |
1142 | + if (ret < 0 && (errno == EINTR || errno == EAGAIN)) | |
1143 | + continue; | |
1005 | 1144 | |
1006 | -error: | |
1007 | - return -1; | |
1008 | -} | |
1145 | + if (ret < 0) { | |
1146 | + ALOGE("dial_at_modem: Error reading from serial port: %d",errno); | |
1147 | + close(fd); | |
1148 | + return -1; | |
1149 | + } | |
1009 | 1150 | |
1151 | + if (ret == 0) { | |
1152 | + waitc --; | |
1153 | + if (waitc <= 0) { | |
1154 | + ALOGE("dial_at_modem: No answer from modem"); | |
1155 | + close(fd); | |
1156 | + return -1; | |
1157 | + } | |
1010 | 1158 | |
1011 | -//static char userPassStatic[512] = "preload"; | |
1159 | + // not timed out yet, sleep a second | |
1160 | + sleep(1); | |
1161 | + } else { | |
1162 | + /* Something received, restart timeout */ | |
1163 | + waitc = 5; | |
1164 | + } | |
1012 | 1165 | |
1013 | -static void requestSetupDefaultPDP(void *data, size_t datalen, RIL_Token t) | |
1014 | -{ | |
1015 | - const char *apn; | |
1016 | - const char *user = NULL; | |
1017 | - const char *pass = NULL; | |
1018 | - char pppdcmd[512] = "/system/bin/pppd "; | |
1019 | - int err; | |
1020 | - int fd, pppstatus,i; | |
1021 | - FILE *pppconfig; | |
1022 | - size_t cur = 0; | |
1023 | - ssize_t written, rlen; | |
1024 | - char status[32] = {0}; | |
1025 | - char *buffer; | |
1026 | - long buffSize, len; | |
1027 | - int retry = 10; | |
1028 | - | |
1029 | - int n = 1; | |
1030 | - RIL_Data_Call_Response_v6 responses; | |
1031 | - char ppp_dnses[(PROPERTY_VALUE_MAX * 2) + 3] = {'\0'}; | |
1032 | - char ppp_local_ip[PROPERTY_VALUE_MAX] = {'\0'}; | |
1033 | - char ppp_dns1[PROPERTY_VALUE_MAX] = {'\0'}; | |
1034 | - char ppp_dns2[PROPERTY_VALUE_MAX] = {'\0'}; | |
1035 | - char ppp_gw[PROPERTY_VALUE_MAX] = {'\0'}; | |
1166 | + // Count of read chars | |
1167 | + n += ret; | |
1168 | + } while (n < 64 && buf[n-1] != '\n'); | |
1036 | 1169 | |
1037 | - apn = ((const char **)data)[2]; | |
1038 | - user = ((char **)data)[3]; | |
1039 | - if (user == NULL || strlen(user) < 2) { | |
1040 | - user = "dummy"; | |
1041 | - } | |
1170 | + /*buf[n] = 0; ALOGD("dial_at_modem: read %d bytes ['%s']",n,&buf[0]);*/ | |
1042 | 1171 | |
1043 | - pass = ((char **)data)[4]; | |
1044 | - if (pass == NULL || strlen(pass) < 2) { | |
1045 | - pass = "dummy"; | |
1046 | - } | |
1172 | + // Remove the trailing spaces | |
1173 | + while (n > 0 && buf[n-1] <= ' ' && buf[n-1] != 0) | |
1174 | + n--; | |
1175 | + buf[n] = 0; | |
1047 | 1176 | |
1048 | - D("requesting data connection to APN '%s'", apn); | |
1177 | + // Remove the leading spaces | |
1178 | + n = 0; | |
1179 | + while (buf[n]<= ' ' && buf[n] != 0) | |
1180 | + n++; | |
1049 | 1181 | |
1050 | - // Make sure there is no existing connection or pppd instance running | |
1051 | - if (killConn("1") < 0) { | |
1052 | - ALOGE("killConn Error!"); | |
1053 | - goto error; | |
1054 | - } | |
1182 | + } while (buf[0] == 0); // Ignore blank lines | |
1055 | 1183 | |
1056 | - /* Switch radio ON */ | |
1057 | - err = at_send_command("AT^RFSWITCH=1"); | |
1058 | - err = at_send_command("AT+CFUN=1"); | |
1184 | + close(fd); | |
1059 | 1185 | |
1060 | - /* Define the PDP context */ | |
1061 | - err = at_send_command("AT+CGDCONT=1,\"IP\",\"%s\",,0,0", apn); | |
1186 | + ALOGD("dial_at_modem: got answer : '%s'",&buf[n]); | |
1187 | + if (strstr(&buf[n],"OK")) return 0; | |
1188 | + if (strstr(&buf[n],"CONNECT")) return 0; | |
1062 | 1189 | |
1063 | - /* Set required QoS params to default */ | |
1064 | - err = at_send_command("AT+CGQREQ=1"); | |
1190 | + return -1; | |
1191 | +} | |
1065 | 1192 | |
1066 | - /* Set minimum QoS params to default */ | |
1067 | - err = at_send_command("AT+CGQMIN=1"); | |
1193 | +static int killConn(const char* cididx) | |
1194 | +{ | |
1195 | + /* Leave NDIS mode */ | |
1196 | + if (!pppSupported()) | |
1197 | + at_send_command("AT^NDISDUP=%s,0",cididx); | |
1068 | 1198 | |
1069 | - /* packet-domain event reporting */ | |
1070 | - err = at_send_command("AT+CGEREP=1,0"); | |
1199 | + /* Leave Data context mode */ | |
1200 | + at_send_command("AT+CGACT=0,%s", cididx); | |
1071 | 1201 | |
1072 | - /* Hangup anything that's happening there now */ | |
1073 | - err = at_send_command("AT+CGACT=0,1"); | |
1202 | + /* Hang up */ | |
1203 | + at_send_command("ATH"); | |
1074 | 1204 | |
1075 | - /* Start data on PDP context 1 */ | |
1076 | - err = at_send_command("ATD*99***1#"); | |
1077 | -// if (err != AT_NOERROR) { | |
1078 | -// goto error; | |
1079 | -// } | |
1080 | - sleep(2); //Wait for the modem to finish | |
1205 | + /* Kill pppd daemon (just in case)*/ | |
1206 | + system("killall pppd"); | |
1081 | 1207 | |
1082 | - // set up the pap/chap secrets file | |
1083 | -// sprintf(userpass, "%s * %s", user, pass); | |
1208 | + /* Hang up modem, if needed */ | |
1209 | + //dial_at_modem("+++",1); | |
1210 | + dial_at_modem("ATH\r\n",1); | |
1084 | 1211 | |
1085 | - /* start the gprs pppd */ | |
1086 | -#if 1 | |
1087 | - property_get("rild.ppp.tty", pppdcmd + strlen(pppdcmd), "/dev/ttyUSB0"); | |
1088 | - strcat(pppdcmd, " call gprs"); | |
1089 | - system(pppdcmd); | |
1090 | -#else | |
1091 | - // Requires root access... | |
1092 | - property_set("ctl.start", "ppp"); | |
1093 | - if (wait_for_property("init.svc.ppp", "running", 10) < 0) { | |
1094 | - ALOGE("Timeout waiting init.svc.ppp - giving up!"); | |
1095 | - goto error; | |
1212 | + /* Bring down all interfaces */ | |
1213 | + if (ifc_init() == 0) { | |
1214 | + ifc_down(RNDIS_IFACE); | |
1215 | + ifc_down(PPP_IFACE); | |
1216 | + ifc_close(); | |
1096 | 1217 | } |
1097 | -#endif | |
1098 | 1218 | |
1099 | - sleep(10); // Allow time for ip-up to complete | |
1219 | + return 0; | |
1220 | +} | |
1100 | 1221 | |
1101 | - if (wait_for_property("net.ppp0.local-ip", NULL, 10) < 0) { | |
1102 | - ALOGE("Timeout waiting net.ppp0.local-ip - giving up!"); | |
1103 | - goto error; | |
1104 | - } | |
1222 | +#define LOG_FILE_DIR "/dev/log/" | |
1105 | 1223 | |
1106 | - property_get("net.ppp0.local-ip", ppp_local_ip, NULL); | |
1107 | - property_get("net.dns1", ppp_dns1, NULL); | |
1108 | - property_get("net.dns2", ppp_dns2, NULL); | |
1109 | - property_get("net.ppp0.gw", ppp_gw, NULL); | |
1110 | - sprintf(ppp_dnses, "%s %s", ppp_dns1, ppp_dns2); | |
1224 | +struct lc_entry { | |
1225 | + union { | |
1226 | + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); | |
1227 | + struct logger_entry entry __attribute__((aligned(4))); | |
1228 | + }; | |
1229 | +}; | |
1111 | 1230 | |
1112 | - ALOGI("Got net.ppp0.local-ip: %s", ppp_local_ip); | |
1231 | +/* Get the connection data used by pppd from the android logcat */ | |
1232 | +int get_pppd_info(struct timeval* from, char* local_ip, char* dns1, char* dns2, char* gw) | |
1233 | +{ | |
1234 | + struct lc_entry* lce; | |
1113 | 1235 | |
1114 | - responses.status = 0; | |
1115 | - responses.suggestedRetryTime = -1; | |
1116 | - responses.cid = 1; | |
1117 | - responses.active = 2; | |
1118 | - responses.type = (char*)"PPP"; | |
1119 | - responses.ifname = (char*)PPP_TTY_PATH; | |
1120 | - responses.addresses = ppp_local_ip; | |
1121 | - responses.dnses = ppp_dnses; | |
1122 | - responses.gateways = ppp_gw; | |
1236 | + const char mainlog[] = LOG_FILE_DIR "main"; | |
1237 | + const char pppd_str[] = "pppd"; | |
1238 | + const char local_ip_str[] = "local IP address "; | |
1239 | + const char remote_ip_str[] = "remote IP address "; | |
1240 | + const char primary_dns_str[] = "primary DNS address "; | |
1241 | + const char secondary_dns_str[] = "secondary DNS address "; | |
1123 | 1242 | |
1124 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, &responses, sizeof(RIL_Data_Call_Response_v6)); | |
1125 | - return; | |
1243 | + int fd; | |
1244 | + int result; | |
1245 | + fd_set readset; | |
1246 | + int search_tmout = 500; // | |
1126 | 1247 | |
1127 | -error: | |
1128 | - ALOGE("Unable to setup PDP"); | |
1129 | - RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
1248 | + local_ip[0] = 0; | |
1249 | + dns1[0] = 0; | |
1250 | + dns2[0] = 0; | |
1251 | + gw[0] = 0; | |
1130 | 1252 | |
1131 | -} | |
1253 | + lce = malloc(sizeof(struct lc_entry)); | |
1254 | + if (!lce) { | |
1255 | + ALOGE("Failed to allocate logcat entry buffer"); | |
1256 | + return -1; | |
1257 | + } | |
1132 | 1258 | |
1133 | -/* CHECK There are several error cases if PDP deactivation fails | |
1134 | - * 24.008: 8, 25, 36, 38, 39, 112 | |
1135 | - */ | |
1136 | -static void requestDeactivateDefaultPDP(void *data, size_t datalen, RIL_Token t) | |
1137 | -{ | |
1138 | - char * cid; | |
1259 | + fd = open(mainlog, O_RDONLY); | |
1260 | + if (fd < 0) { | |
1261 | + ALOGE("Unable to open log device '%s': %s", mainlog, strerror(errno)); | |
1262 | + free(lce); | |
1263 | + return -1; | |
1264 | + } | |
1139 | 1265 | |
1140 | - D("requestDeactivateDefaultPDP()"); | |
1266 | + /*ALOGD("filter: sec:%d, usec:%d", from->tv_sec, from->tv_usec);*/ | |
1141 | 1267 | |
1142 | - cid = ((char **)data)[0]; | |
1143 | - if (killConn(cid) < 0) | |
1144 | - goto error; | |
1268 | + while (1) { | |
1145 | 1269 | |
1146 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); | |
1270 | + do { | |
1271 | + struct timeval timeout = { 0, 10000 /* 10ms */ }; | |
1272 | + FD_ZERO(&readset); | |
1273 | + FD_SET(fd, &readset); | |
1274 | + result = select(fd + 1, &readset, NULL, NULL, &timeout); | |
1275 | + } while (result < 0 && errno == EINTR); | |
1276 | + | |
1277 | + // Wait until we find something... | |
1278 | + if (result == 0) { | |
1279 | + if (search_tmout > 0) { | |
1280 | + search_tmout --; | |
1281 | + continue; | |
1282 | + } else | |
1283 | + break; | |
1284 | + } | |
1285 | + | |
1286 | + if (result > 0 && FD_ISSET(fd, &readset)) { | |
1287 | + | |
1288 | + /* NOTE: driver guarantees we read exactly one full entry */ | |
1289 | + int ret = read(fd, lce, LOGGER_ENTRY_MAX_LEN); | |
1290 | + if (ret < 0) { | |
1291 | + if (errno == EINTR) { | |
1292 | + continue; | |
1293 | + } | |
1294 | + if (errno == EAGAIN) { | |
1295 | + continue; | |
1296 | + } | |
1297 | + ALOGE("logcat read"); | |
1298 | + continue; | |
1299 | + } | |
1300 | + else if (!ret) { | |
1301 | + ALOGE("read: Unexpected EOF!"); | |
1302 | + continue; | |
1303 | + } | |
1304 | + else if (lce->entry.len != ret - sizeof(struct logger_entry)) { | |
1305 | + ALOGE("read: unexpected length. Expected %d, got %d", | |
1306 | + lce->entry.len, ret - sizeof(struct logger_entry)); | |
1307 | + continue; | |
1308 | + } | |
1309 | + | |
1310 | + lce->entry.msg[lce->entry.len] = '\0'; | |
1311 | + | |
1312 | + /* | |
1313 | + * format: <priority:1><tag:N>\0<message:N>\0 | |
1314 | + * | |
1315 | + * tag str | |
1316 | + * starts at buf->msg+1 | |
1317 | + * msg | |
1318 | + * starts at buf->msg+1+len(tag)+1 | |
1319 | + * | |
1320 | + * The message may have been truncated by the kernel log driver. | |
1321 | + * When that happens, we must null-terminate the message ourselves. | |
1322 | + */ | |
1323 | + | |
1324 | + /*ALOGD("entry: tag:%d[%c], sec:%d, usec:%d, n:%s, m:%s", lce->entry.msg[0], lce->entry.msg[0], lce->entry.sec, lce->entry.nsec/1000, &lce->entry.msg[1],(&lce->entry.msg[1])+strlen(&lce->entry.msg[1])+1); */ | |
1325 | + | |
1326 | + // We are interested in pppd entries with I priority newer than specified | |
1327 | + if ((lce->entry.msg[0] == 'i' || lce->entry.msg[0] == 'I' || lce->entry.msg[0] == 4) && | |
1328 | + (lce->entry.sec > from->tv_sec || | |
1329 | + (lce->entry.sec == from->tv_sec && (lce->entry.nsec/1000) >= from->tv_usec)) && | |
1330 | + !strcmp(&lce->entry.msg[1],pppd_str)) { | |
1331 | + char * pos = NULL; | |
1332 | + | |
1333 | + // Dealing with PPPD entries in logcat - Get a pointer to the message | |
1334 | + char * msg = (&lce->entry.msg[1]) + strlen(&lce->entry.msg[1]) + 1; | |
1335 | + | |
1336 | + // Strip leading spaces | |
1337 | + while (*msg != 0 && *msg <= ' ') msg++; | |
1338 | + | |
1339 | + // And also strip trailing spaces... | |
1340 | + pos = msg + strlen(msg); | |
1341 | + while (pos != msg && pos[-1] <= ' ') pos--; | |
1342 | + *pos = 0; | |
1343 | + | |
1344 | + // An entry we are interested in ? | |
1345 | + if ((pos = strstr(msg,local_ip_str)) != NULL) { | |
1346 | + strcpy(local_ip,msg + sizeof(local_ip_str) - 1); | |
1347 | + strcpy(gw,msg + sizeof(local_ip_str) - 1); | |
1348 | + } else | |
1349 | + if ((pos = strstr(msg,remote_ip_str)) != NULL) { | |
1350 | + // We found the remote_ip address ... Give no more than 3 seconds for the next entry to appear | |
1351 | + // (those entries are not mandatory) | |
1352 | + search_tmout = 300; | |
1353 | + } else | |
1354 | + if ((pos = strstr(msg,primary_dns_str)) != NULL) { | |
1355 | + strcpy(dns1,msg + sizeof(primary_dns_str) - 1); | |
1356 | + } else | |
1357 | + if ((pos = strstr(msg,secondary_dns_str)) != NULL) { | |
1358 | + strcpy(dns2,msg + sizeof(secondary_dns_str) - 1); | |
1359 | + } | |
1360 | + } | |
1361 | + | |
1362 | + // If we have all required data, break now! | |
1363 | + if (local_ip[0] && gw[0] && dns1[0] && dns2[0]) { | |
1364 | + break; | |
1365 | + } | |
1366 | + } | |
1367 | + } | |
1368 | + | |
1369 | + close(fd); | |
1370 | + free(lce); | |
1371 | + | |
1372 | + // Return if we succeeded or not | |
1373 | + return local_ip[0] ? 0 : -1; | |
1374 | +} | |
1375 | + | |
1376 | + | |
1377 | +/* Setup connection using PPP */ | |
1378 | +static int setupPPP(RIL_Token t,const char* ctxid,const char* user,const char* pass) | |
1379 | +{ | |
1380 | + int err; | |
1381 | + char* cmd = NULL; | |
1382 | + const char* fmt; | |
1383 | + in_addr_t addr; | |
1384 | + in_addr_t mask; | |
1385 | + unsigned flags = 0; | |
1386 | + int ctr = 10; | |
1387 | + struct timeval from_tm; | |
1388 | + | |
1389 | + RIL_Data_Call_Response_v6 responses; | |
1390 | + char ppp_ifname[PROPERTY_VALUE_MAX] = {'\0'}; | |
1391 | + char ppp_local_ip[PROPERTY_VALUE_MAX] = {'\0'}; | |
1392 | + char ppp_dns1[PROPERTY_VALUE_MAX] = {'\0'}; | |
1393 | + char ppp_dns2[PROPERTY_VALUE_MAX] = {'\0'}; | |
1394 | + char ppp_dnses[(PROPERTY_VALUE_MAX * 2) + 3] = {'\0'}; | |
1395 | + char ppp_gw[PROPERTY_VALUE_MAX] = {'\0'}; | |
1396 | + char pbuf[32] = {'\0'}; | |
1397 | + | |
1398 | + | |
1399 | + ALOGD("Trying to setup PPP connnection..."); | |
1400 | + | |
1401 | +#if 1 | |
1402 | + /* PDP context activation */ | |
1403 | + //err = at_send_command("AT+CGACT=0,%s",ctxid); | |
1404 | + err = at_send_command("ATD*99***%s#", ctxid); | |
1405 | +#else | |
1406 | + /* Try just with data context activation */ | |
1407 | + asprintf(&cmd,"AT+CGDATA=\"PPP\",%s\r\n",ctxid); | |
1408 | + err = dial_at_modem(cmd,0); | |
1409 | + free(cmd); | |
1410 | + if (err) { | |
1411 | + /* If failed, retry with dial command */ | |
1412 | + asprintf(&cmd,"ATD*99***%s#\r\n",ctxid); | |
1413 | + err = dial_at_modem(cmd,0); | |
1414 | + free(cmd); | |
1415 | + if (err) { | |
1416 | + ALOGE("Failed to dial"); | |
1417 | + return -1; | |
1418 | + } | |
1419 | + } | |
1420 | +#endif | |
1421 | + | |
1422 | + /* Wait for the modem to finish */ | |
1423 | + sleep(2); | |
1424 | + | |
1425 | + /* original options | |
1426 | + "nodetach debug noauth defaultroute usepeerdns " | |
1427 | + "connect-delay 1000 " | |
1428 | + "user NotUsed@nobody.com password NotUsed " | |
1429 | + "crtscts lcp-echo-failure 0 lcp-echo-interval 0 ipcp-max-configure 30 " | |
1430 | + "ipcp-max-failure 30 ipcp-max-terminate 10 novj linkname ril"; | |
1431 | + */ | |
1432 | + | |
1433 | +#if 0 | |
1434 | + /* start the gprs pppd - pppd must be suid root */ | |
1435 | + if (user == NULL || user[0] == 0 || | |
1436 | + pass == NULL || pass[0] == 0 ) { | |
1437 | + | |
1438 | + fmt = "/system/bin/pppd %s " | |
1439 | + "115200 " | |
1440 | + "crtscts modem " | |
1441 | + "linkname ril%s " | |
1442 | + "noauth " | |
1443 | + "user %s password %s " | |
1444 | + "defaultroute usepeerdns noipdefault " | |
1445 | + "novj novjccomp nobsdcomp " | |
1446 | + "ipcp-accept-remote ipcp-accept-local " | |
1447 | + "dump debug " | |
1448 | + "lcp-echo-failure 0 lcp-echo-interval 0 ipcp-max-configure 30 " | |
1449 | + "ipcp-max-failure 30 ipcp-max-terminate 10"; | |
1450 | + | |
1451 | + } else { | |
1452 | + | |
1453 | + fmt = "/system/bin/pppd %s " | |
1454 | + "115200 " | |
1455 | + "crtscts modem " | |
1456 | + "linkname ril%s " | |
1457 | + "user %s password %s " | |
1458 | + "defaultroute usepeerdns noipdefault " | |
1459 | + "novj novjccomp nobsdcomp " | |
1460 | + "ipcp-accept-remote ipcp-accept-local " | |
1461 | + "dump debug " | |
1462 | + "lcp-echo-failure 0 lcp-echo-interval 0 ipcp-max-configure 30 " | |
1463 | + "ipcp-max-failure 30 ipcp-max-terminate 10"; | |
1464 | + } | |
1465 | + | |
1466 | + asprintf(&cmd,fmt,ppp_iface_dev,ctxid, ((user && user[0])? user: "guest"), ((pass && pass[0])? pass: "guest") ); | |
1467 | +#else | |
1468 | + fmt = "/system/bin/pppd %s call gprs"; | |
1469 | + asprintf(&cmd, fmt, ppp_iface_dev); | |
1470 | +#endif | |
1471 | + | |
1472 | + ALOGD("Starting pppd w/command line: '%s'", cmd); | |
1473 | + | |
1474 | + // Get current time | |
1475 | + gettimeofday(&from_tm,NULL); | |
1476 | + | |
1477 | + // Start PPPD | |
1478 | + system(cmd); | |
1479 | + free(cmd); | |
1480 | + | |
1481 | +#if 0 | |
1482 | + /* Try not to depend on ip-up/down scripts ... */ | |
1483 | + | |
1484 | + // Wait until network interface is up and running | |
1485 | + if (ifc_init() < 0) { | |
1486 | + ALOGE("Failed initialization of net ifc"); | |
1487 | + return -1; | |
1488 | + } | |
1489 | + | |
1490 | + ALOGD("Waiting until net ifc %s is up", PPP_IFACE); | |
1491 | + ctr = 20; | |
1492 | + do { | |
1493 | + sleep(1); // wait one second... | |
1494 | + flags = 0; | |
1495 | + ifc_get_info(PPP_IFACE, &addr, &mask, &flags); | |
1496 | + } while (--ctr > 0 && !(flags & IFF_UP)); | |
1497 | + ifc_close(); | |
1498 | + | |
1499 | + if (ctr <= 0) { | |
1500 | + ALOGE("Net ifc %s was never upped!", PPP_IFACE); | |
1501 | + return -1; | |
1502 | + } | |
1503 | + | |
1504 | + /* Get PPP information by reading and parsing the PPPD log */ | |
1505 | + if (get_pppd_info(&from_tm, ppp_local_ip, ppp_dns1, ppp_dns2, ppp_gw) < 0) { | |
1506 | + ALOGE("Unable to get dns/gw"); | |
1507 | + return -1; | |
1508 | + } | |
1509 | + | |
1510 | + strcpy(ppp_ifname,PPP_IFACE); | |
1511 | + | |
1512 | +#elif 0 | |
1513 | + // Wait until ppp ip-up script has completely executed... | |
1514 | + sprintf(pbuf,"net.ril%s.if",ctxid); | |
1515 | + if (wait_for_property(pbuf, NULL, 20, 0) < 0) { | |
1516 | + ALOGE("Timeout waiting %s - giving up!", pbuf); | |
1517 | + return -1; | |
1518 | + } | |
1519 | + property_get(pbuf, ppp_ifname, NULL); | |
1520 | + | |
1521 | + // Get local IP | |
1522 | + sprintf(pbuf,"net.ril%s.local-ip",ctxid); | |
1523 | + property_get(pbuf, ppp_local_ip, NULL); | |
1524 | + | |
1525 | + sprintf(pbuf,"net.ril%s.dns1",ctxid); | |
1526 | + property_get(pbuf, ppp_dns1, NULL); | |
1527 | + | |
1528 | + sprintf(pbuf,"net.ril%s.dns2",ctxid); | |
1529 | + property_get(pbuf, ppp_dns2, NULL); | |
1530 | + | |
1531 | + sprintf(pbuf,"net.ril%s.gw",ctxid); | |
1532 | + property_get(pbuf, ppp_gw, NULL); | |
1533 | + | |
1534 | +#else | |
1535 | + strcpy(ppp_ifname,PPP_IFACE); | |
1536 | + strcpy(pbuf, "net." PPP_IFACE ".local-ip"); | |
1537 | + if (wait_for_property(pbuf, NULL, 20, 0) < 0) { | |
1538 | + ALOGE("Timeout waiting %s - giving up!", pbuf); | |
1539 | + return -1; | |
1540 | + } | |
1541 | + property_get("net.ppp0.local-ip", ppp_local_ip, NULL); | |
1542 | + property_get("net.ppp0.gw", ppp_gw, NULL); | |
1543 | + property_get("net.dns1", ppp_dns1, NULL); | |
1544 | + property_get("net.dns2", ppp_dns2, NULL); | |
1545 | + | |
1546 | +#endif | |
1547 | + | |
1548 | + sprintf(ppp_dnses, "%s %s", ppp_dns1, ppp_dns2); | |
1549 | + | |
1550 | + ALOGI("Got ifname: %s, local-ip: %s, dns1: %s, dns2: %s, gw: %s", | |
1551 | + ppp_ifname, ppp_local_ip, ppp_dns1, ppp_dns2, ppp_gw); | |
1552 | + | |
1553 | + responses.status = 0; | |
1554 | + responses.suggestedRetryTime = -1; | |
1555 | + responses.cid = (ctxid[0] - '0'); | |
1556 | + responses.active = 2; | |
1557 | + responses.type = (char*)"PPP"; | |
1558 | + responses.ifname = ppp_ifname; | |
1559 | + responses.addresses = ppp_local_ip; | |
1560 | + responses.dnses = ppp_dnses; | |
1561 | + responses.gateways = ppp_gw; | |
1562 | + | |
1563 | + using_rndis = 0; | |
1564 | + | |
1565 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, &responses, sizeof(RIL_Data_Call_Response_v6)); | |
1566 | + return 0; | |
1567 | +} | |
1568 | + | |
1569 | +/* Converts a hex encoded addr to an std addr */ | |
1570 | +static void str2addr(char* addr,const char* str) | |
1571 | +{ | |
1572 | + unsigned int val = (unsigned int) strtoul(str, NULL, 16); | |
1573 | + sprintf(addr,"%u.%u.%u.%u", | |
1574 | + (val & 0x000000ffU), | |
1575 | + (val & 0x0000ff00U) >> 8U, | |
1576 | + (val & 0x00ff0000U) >> 16U, | |
1577 | + (val & 0xff000000U) >> 24U); | |
1578 | +} | |
1579 | + | |
1580 | +/* Setup connection using NDIS */ | |
1581 | +static int setupNDIS(RIL_Token t,const char* ctxid) | |
1582 | +{ | |
1583 | + ATResponse *atResponse = NULL; | |
1584 | + RIL_Data_Call_Response_v6 responses; | |
1585 | + char rndis_dnses[64 + 3] = {'\0'}; | |
1586 | + char rndis_local_ip[32] = {'\0'}; | |
1587 | + char rndis_dns1[32] = {'\0'}; | |
1588 | + char rndis_dns2[32] = {'\0'}; | |
1589 | + char rndis_gw[32] = {'\0'}; | |
1590 | + | |
1591 | + in_addr_t in_addr; | |
1592 | + in_addr_t in_gateway; | |
1593 | + | |
1594 | + int err; | |
1595 | + char *line; | |
1596 | + char *ip = NULL; | |
1597 | + char *netmask = NULL; | |
1598 | + char *gateway = NULL; | |
1599 | + char *unk = NULL; | |
1600 | + char *dns1 = NULL; | |
1601 | + char *dns2 = NULL; | |
1602 | + | |
1603 | + ALOGD("Trying to setup NDIS connnection..."); | |
1604 | + | |
1605 | + /* Enter data state using context #1 as NDIS */ | |
1606 | + err = at_send_command("AT^NDISDUP=%s,1",ctxid); | |
1607 | + if (err != AT_NOERROR) | |
1608 | + return -1; | |
1609 | + | |
1610 | + /* Leave 10 seconds for startup */ | |
1611 | + sleep(10); | |
1612 | + | |
1613 | + /* Check DHCP status */ | |
1614 | + err = at_send_command_singleline("AT^DHCP?", "^DHCP:", &atResponse); | |
1615 | + if (err != AT_NOERROR) | |
1616 | + goto error; | |
1617 | + | |
1618 | + line = atResponse->p_intermediates->line; | |
1619 | + err = at_tok_start(&line); | |
1620 | + if (err < 0) goto error; | |
1621 | + | |
1622 | + err = at_tok_nextstr(&line, &ip); | |
1623 | + if (err < 0) goto error; | |
1624 | + | |
1625 | + err = at_tok_nextstr(&line, &netmask); | |
1626 | + if (err < 0) goto error; | |
1627 | + | |
1628 | + err = at_tok_nextstr(&line, &gateway); | |
1629 | + if (err < 0) goto error; | |
1630 | + | |
1631 | + err = at_tok_nextstr(&line, &unk); | |
1632 | + if (err < 0) goto error; | |
1633 | + | |
1634 | + err = at_tok_nextstr(&line, &dns1); | |
1635 | + if (err < 0) goto error; | |
1636 | + | |
1637 | + err = at_tok_nextstr(&line, &dns2); | |
1638 | + if (err < 0) goto error; | |
1639 | + | |
1640 | + ALOGD("IP: %s, netmask: %s, gateway: %s, dns1:%s, dns2:%s",ip,netmask,gateway,dns1,dns2); | |
1641 | + | |
1642 | + str2addr(rndis_local_ip, ip); | |
1643 | + if (inet_pton(AF_INET, rndis_local_ip, &in_addr) <= 0) { | |
1644 | + ALOGE("%s() inet_pton() failed for %s!", __func__, rndis_local_ip); | |
1645 | + goto error; | |
1646 | + } | |
1647 | + | |
1648 | + str2addr(rndis_dns1, dns1); | |
1649 | + str2addr(rndis_dns2, dns2); | |
1650 | + sprintf(rndis_dnses, "%s %s", rndis_dns1, rndis_dns2); | |
1651 | + | |
1652 | + str2addr(rndis_gw, gateway); | |
1653 | + if (inet_pton(AF_INET, rndis_gw, &in_gateway) <= 0) { | |
1654 | + ALOGE("%s() inet_pton() failed for %s!", __func__, rndis_gw); | |
1655 | + goto error; | |
1656 | + } | |
1657 | + | |
1658 | + at_response_free(atResponse); | |
1659 | + | |
1660 | + responses.status = 0; | |
1661 | + responses.suggestedRetryTime = -1; | |
1662 | + responses.cid = 1; | |
1663 | + responses.active = 2; | |
1664 | + responses.type = (char*)"RNDIS"; | |
1665 | + responses.ifname = (char*)RNDIS_IFACE; | |
1666 | + responses.addresses = rndis_local_ip; | |
1667 | + responses.dnses = rndis_dnses; | |
1668 | + responses.gateways = rndis_gw; | |
1669 | + | |
1670 | + /* Don't use android netutils. We use our own and get the routing correct. | |
1671 | + * Carl Nordbeck */ | |
1672 | + if (ifc_configure(RNDIS_IFACE, in_addr, in_gateway)) | |
1673 | + ALOGE("%s() Failed to configure the interface %s", __func__, RNDIS_IFACE); | |
1674 | + | |
1675 | + using_rndis = 1; | |
1676 | + | |
1677 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, &responses, sizeof(RIL_Data_Call_Response_v6)); | |
1678 | + return 0; | |
1679 | + | |
1680 | +error: | |
1681 | + ALOGE("Failed to start NDIS mode"); | |
1682 | + | |
1683 | + at_response_free(atResponse); | |
1684 | + return -1; | |
1685 | +} | |
1686 | + | |
1687 | +/* Query if modem supports PPP. Otherwise, RNDIS will be used */ | |
1688 | +static int pppSupported(void) | |
1689 | +{ | |
1690 | + ATResponse *atResponse = NULL; | |
1691 | + | |
1692 | + int err; | |
1693 | + int supp_mode = 0; // PPP | |
1694 | + char *line; | |
1695 | + | |
1696 | + ALOGD("Identifying modem capabilities..."); | |
1697 | + | |
1698 | + /* Check Modem capabilities */ | |
1699 | + err = at_send_command_singleline("AT^DIALMODE?", "^DIALMODE:", &atResponse); | |
1700 | + if (err != AT_NOERROR) | |
1701 | + goto error; | |
1702 | + | |
1703 | + line = atResponse->p_intermediates->line; | |
1704 | + err = at_tok_start(&line); | |
1705 | + if (err < 0) goto error; | |
1706 | + | |
1707 | + err = at_tok_nextint(&line, &supp_mode); | |
1708 | + if (err < 0) goto error; | |
1709 | + | |
1710 | + ALOGD("Modem mode: %d [0=ppp,1=ndis,2=ppp&ndis]",supp_mode); | |
1711 | + | |
1712 | + at_response_free(atResponse); | |
1713 | + | |
1714 | + return supp_mode != 1; // ppp supported | |
1715 | + | |
1716 | +error: | |
1717 | + ALOGD("Assuming PPP mode"); | |
1718 | + at_response_free(atResponse); | |
1719 | + return 1; | |
1720 | +} | |
1721 | + | |
1722 | +static void requestSetupDefaultPDP(void *data, size_t datalen, RIL_Token t) | |
1723 | +{ | |
1724 | + const char *apn; | |
1725 | + const char *user = NULL; | |
1726 | + const char *pass = NULL; | |
1727 | + int err; | |
1728 | + const char* ctxid = "1"; | |
1729 | + int use_ppp; | |
1730 | + | |
1731 | + apn = ((const char **)data)[2]; | |
1732 | + user = ((char **)data)[3]; | |
1733 | + if (user == NULL || strlen(user) < 2) { | |
1734 | + user = "dummy"; | |
1735 | + } | |
1736 | + | |
1737 | + pass = ((char **)data)[4]; | |
1738 | + if (pass == NULL || strlen(pass) < 2) { | |
1739 | + pass = "dummy"; | |
1740 | + } | |
1741 | + | |
1742 | + /* Check if PPP is supported */ | |
1743 | + use_ppp = pppSupported(); | |
1744 | + | |
1745 | + D("requesting data connection to APN '%s' use_ppp=%d", apn, use_ppp); | |
1746 | + | |
1747 | + // Make sure there is no existing connection | |
1748 | + killConn(ctxid); | |
1749 | + | |
1750 | + | |
1751 | + /* packet-domain event reporting */ | |
1752 | + err = at_send_command("AT+CGEREP=1,0"); | |
1753 | + | |
1754 | + /* Define the PDP context #1*/ | |
1755 | + err = at_send_command("AT+CGDCONT=%s,\"IP\",\"%s\"",ctxid, apn); | |
1756 | + | |
1757 | + /* Set required QoS params to default */ | |
1758 | + err = at_send_command("AT+CGQREQ=%s",ctxid); | |
1759 | + | |
1760 | + /* Set minimum QoS params to default */ | |
1761 | + err = at_send_command("AT+CGQMIN=%s",ctxid); | |
1762 | + | |
1763 | + /* Attach to GPRS network */ | |
1764 | + err = at_send_command("AT+CGATT=1"); | |
1765 | + | |
1766 | + if (use_ppp) { | |
1767 | + if (setupPPP(t,ctxid,user,pass) < 0) { | |
1768 | + goto error; | |
1769 | + } | |
1770 | + } else { | |
1771 | + if (setupNDIS(t,ctxid) < 0) { | |
1772 | + goto error; | |
1773 | + } | |
1774 | + } | |
1775 | + | |
1776 | + ALOGD("Data connection established!"); | |
1777 | + return; | |
1778 | + | |
1779 | +error: | |
1780 | + ALOGE("Unable to setup PDP"); | |
1781 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
1782 | + | |
1783 | +} | |
1784 | + | |
1785 | +/* CHECK There are several error cases if PDP deactivation fails | |
1786 | + * 24.008: 8, 25, 36, 38, 39, 112 | |
1787 | + */ | |
1788 | +static void requestDeactivateDefaultPDP(void *data, size_t datalen, RIL_Token t) | |
1789 | +{ | |
1790 | + char * cid; | |
1791 | + | |
1792 | + D("requestDeactivateDefaultPDP()"); | |
1793 | + | |
1794 | + cid = ((char **)data)[0]; | |
1795 | + if (killConn(cid) < 0) | |
1796 | + goto error; | |
1797 | + | |
1798 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); | |
1147 | 1799 | return; |
1148 | 1800 | |
1149 | 1801 | error: |
@@ -1204,7 +1856,7 @@ static void requestOrSendPDPContextList(RIL_Token *t) | ||
1204 | 1856 | responses[i].cid = -1; |
1205 | 1857 | responses[i].active = -1; |
1206 | 1858 | responses[i].type = (char*)""; |
1207 | - responses[i].ifname = (char*)PPP_TTY_PATH; | |
1859 | + responses[i].ifname = using_rndis ? (char*)RNDIS_IFACE : (char*)PPP_IFACE; | |
1208 | 1860 | responses[i].addresses = (char*)""; |
1209 | 1861 | responses[i].dnses = (char*)""; |
1210 | 1862 | responses[i].gateways = (char*)""; |
@@ -1277,7 +1929,7 @@ static void requestOrSendPDPContextList(RIL_Token *t) | ||
1277 | 1929 | if (err < 0) |
1278 | 1930 | goto error; |
1279 | 1931 | |
1280 | - responses[i].ifname = (char*)PPP_TTY_PATH; | |
1932 | + responses[i].ifname = using_rndis ? (char*)RNDIS_IFACE : (char*)PPP_IFACE; | |
1281 | 1933 | |
1282 | 1934 | err = at_tok_nextstr(&line, &out); |
1283 | 1935 | if (err < 0) |
@@ -1291,6 +1943,27 @@ static void requestOrSendPDPContextList(RIL_Token *t) | ||
1291 | 1943 | at_response_free(atResponse); |
1292 | 1944 | atResponse = NULL; |
1293 | 1945 | |
1946 | + /* Make sure interface is UP and running. If not, invalidate datacall to | |
1947 | + force Android to reconnect - This will happen when resuming from suspend, as pppd | |
1948 | + has probably ended as a consequence of the USB bus being suspended and the terminal | |
1949 | + disappearing. So, that is why we have this check here. Not only the modem has to | |
1950 | + report an active connection, also linux has to export an active net interface !*/ | |
1951 | + if (ifc_init() >= 0) { | |
1952 | + in_addr_t addr; | |
1953 | + in_addr_t mask; | |
1954 | + unsigned flags = 0; | |
1955 | + ifc_get_info( responses[i].ifname, &addr, &mask, &flags); | |
1956 | + if (!(flags & IFF_UP)) { | |
1957 | + for (i = 0; i < n; i++) { | |
1958 | + if (responses[i].active) { | |
1959 | + ALOGD("DataCall cid:%d forced to inactive, as associated interface [%s] is DOWN", responses[i].cid, responses[i].ifname ); | |
1960 | + responses[i].active = 0; | |
1961 | + } | |
1962 | + } | |
1963 | + } | |
1964 | + ifc_close(); | |
1965 | + } | |
1966 | + | |
1294 | 1967 | if (t != NULL) |
1295 | 1968 | RIL_onRequestComplete(*t, RIL_E_SUCCESS, responses, |
1296 | 1969 | n * sizeof(RIL_Data_Call_Response_v6)); |
@@ -1440,13 +2113,12 @@ static struct held_pdu *dequeue_held_pdu(void) | ||
1440 | 2113 | |
1441 | 2114 | static void enqueue_held_pdu(char type, const char *sms_pdu) |
1442 | 2115 | { |
1443 | - struct held_pdu *hpdu = (struct held_pdu *) malloc(sizeof(*hpdu)); | |
2116 | + struct held_pdu *hpdu = (struct held_pdu *) calloc(1, sizeof(*hpdu)); | |
1444 | 2117 | if (hpdu == NULL) { |
1445 | 2118 | ALOGE("%s() failed to allocate memory!", __func__); |
1446 | 2119 | return; |
1447 | 2120 | } |
1448 | 2121 | |
1449 | - memset(hpdu, 0, sizeof(*hpdu)); | |
1450 | 2122 | hpdu->type = type; |
1451 | 2123 | hpdu->sms_pdu = strdup(sms_pdu); |
1452 | 2124 | if (NULL == hpdu->sms_pdu) { |
@@ -1454,9 +2126,9 @@ static void enqueue_held_pdu(char type, const char *sms_pdu) | ||
1454 | 2126 | return; |
1455 | 2127 | } |
1456 | 2128 | |
1457 | - if (s_held_pdus == NULL) | |
2129 | + if (s_held_pdus == NULL) { | |
1458 | 2130 | s_held_pdus = hpdu; |
1459 | - else { | |
2131 | + } else { | |
1460 | 2132 | struct held_pdu *p = s_held_pdus; |
1461 | 2133 | while (p->next != NULL) |
1462 | 2134 | p = p->next; |
@@ -1514,12 +2186,12 @@ exit: | ||
1514 | 2186 | * SIM SMS storage area is full, cannot receive |
1515 | 2187 | * more messages until memory freed |
1516 | 2188 | */ |
1517 | -void onNewSmsIndication(void) | |
2189 | +static void onNewSmsIndication(void) | |
1518 | 2190 | { |
1519 | 2191 | enqueueRILEvent(isSimSmsStorageFull, NULL, NULL); |
1520 | 2192 | } |
1521 | 2193 | |
1522 | -void onNewSms(const char *sms_pdu) | |
2194 | +static void onNewSms(const char *sms_pdu) | |
1523 | 2195 | { |
1524 | 2196 | pthread_mutex_lock(&s_held_pdus_mutex); |
1525 | 2197 |
@@ -1533,13 +2205,13 @@ void onNewSms(const char *sms_pdu) | ||
1533 | 2205 | } else { |
1534 | 2206 | s_outstanding_acknowledge = 1; |
1535 | 2207 | RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NEW_SMS, |
1536 | - sms_pdu, strlen(sms_pdu)); | |
2208 | + sms_pdu, strlen(sms_pdu)); | |
1537 | 2209 | } |
1538 | 2210 | |
1539 | 2211 | pthread_mutex_unlock(&s_held_pdus_mutex); |
1540 | 2212 | } |
1541 | 2213 | |
1542 | -void onNewStatusReport(const char *sms_pdu) | |
2214 | +static void onNewStatusReport(const char *sms_pdu) | |
1543 | 2215 | { |
1544 | 2216 | char *response = NULL; |
1545 | 2217 | int err; |
@@ -1563,13 +2235,13 @@ void onNewStatusReport(const char *sms_pdu) | ||
1563 | 2235 | } else { |
1564 | 2236 | s_outstanding_acknowledge = 1; |
1565 | 2237 | RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT, |
1566 | - response, strlen(response)); | |
2238 | + response, strlen(response)); | |
1567 | 2239 | } |
1568 | 2240 | free(response); |
1569 | 2241 | pthread_mutex_unlock(&s_held_pdus_mutex); |
1570 | 2242 | } |
1571 | 2243 | |
1572 | -void onNewBroadcastSms(const char *pdu) | |
2244 | +static void onNewBroadcastSms(const char *pdu) | |
1573 | 2245 | { |
1574 | 2246 | char *message = NULL; |
1575 | 2247 | D("%s() Length : %d", __func__, strlen(pdu)); |
@@ -1602,7 +2274,7 @@ void onNewBroadcastSms(const char *pdu) | ||
1602 | 2274 | } else { |
1603 | 2275 | s_outstanding_acknowledge = 1; |
1604 | 2276 | RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, |
1605 | - message, BSM_LENGTH); | |
2277 | + message, BSM_LENGTH); | |
1606 | 2278 | } |
1607 | 2279 | |
1608 | 2280 | pthread_mutex_unlock(&s_held_pdus_mutex); |
@@ -1611,7 +2283,7 @@ error: | ||
1611 | 2283 | return; |
1612 | 2284 | } |
1613 | 2285 | |
1614 | -void onNewSmsOnSIM(const char *s) | |
2286 | +static void onNewSmsOnSIM(const char *s) | |
1615 | 2287 | { |
1616 | 2288 | char *line; |
1617 | 2289 | char *mem; |
@@ -1636,8 +2308,14 @@ void onNewSmsOnSIM(const char *s) | ||
1636 | 2308 | if (err < 0) |
1637 | 2309 | goto error; |
1638 | 2310 | |
2311 | + /* Huawei modems use a 0-based message slot index, but the SIM record is 1-based. | |
2312 | + We will translate Huawei indices to Record indices, to make the Android RIL | |
2313 | + able to read SMS stored in SIM using SimIo with the same indices as the Sms | |
2314 | + delete command and NewSmsOnSim index */ | |
2315 | + index += 1; | |
2316 | + | |
1639 | 2317 | RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM, |
1640 | - &index, sizeof(int *)); | |
2318 | + &index, sizeof(int *)); | |
1641 | 2319 | |
1642 | 2320 | finally: |
1643 | 2321 | free(line); |
@@ -1715,9 +2393,9 @@ static void requestSendSMS(void *data, size_t datalen, RIL_Token t) | ||
1715 | 2393 | smsc[4+length] = '\0'; |
1716 | 2394 | } |
1717 | 2395 | at_response_free(atResponse); |
2396 | + } else { | |
2397 | + strcpy(smsc, testSmsc); | |
1718 | 2398 | } |
1719 | - else | |
1720 | - strcpy(smsc,testSmsc); | |
1721 | 2399 | |
1722 | 2400 | asprintf(&cmd1, "AT+CMGS=%d", tpLayerLength); |
1723 | 2401 | asprintf(&cmd2, "%s%s", smsc, pdu); |
@@ -1754,15 +2432,15 @@ finally: | ||
1754 | 2432 | |
1755 | 2433 | error: |
1756 | 2434 | switch (at_get_cms_error(aterr)) { |
1757 | - case CMS_NO_NETWORK_SERVICE: | |
1758 | - case CMS_NETWORK_TIMEOUT: | |
1759 | - ret = RIL_E_SMS_SEND_FAIL_RETRY; | |
1760 | - break; | |
1761 | - default: | |
1762 | - ret = RIL_E_GENERIC_FAILURE; | |
1763 | - break; | |
1764 | - } | |
1765 | - RIL_onRequestComplete(t, ret, NULL, 0); | |
2435 | + case CMS_NO_NETWORK_SERVICE: | |
2436 | + case CMS_NETWORK_TIMEOUT: | |
2437 | + ret = RIL_E_SMS_SEND_FAIL_RETRY; | |
2438 | + break; | |
2439 | + default: | |
2440 | + ret = RIL_E_GENERIC_FAILURE; | |
2441 | + break; | |
2442 | + } | |
2443 | + RIL_onRequestComplete(t, ret, NULL, 0); | |
1766 | 2444 | goto finally; |
1767 | 2445 | } |
1768 | 2446 |
@@ -1845,7 +2523,18 @@ static void requestDeleteSmsOnSim(void *data, size_t datalen, RIL_Token t) | ||
1845 | 2523 | { |
1846 | 2524 | int err; |
1847 | 2525 | |
1848 | - err = at_send_command("AT+CMGD=%d", ((int *) data)[0]); | |
2526 | + /* Huawei modems use a 0-based message slot index, but the SIM record is 1-based. | |
2527 | + We will translate Huawei indices to Record indices, to make the Android RIL | |
2528 | + able to read SMS stored in SIM using SimIo with the same indices as the Sms | |
2529 | + delete command and NewSmsOnSim index */ | |
2530 | + int idx = ((int *) data)[0] - 1; | |
2531 | + if (idx < 0) { | |
2532 | + ALOGE("DeleteSmsOnSim: Invalid index! (%d)",idx); | |
2533 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
2534 | + return; | |
2535 | + } | |
2536 | + | |
2537 | + err = at_send_command("AT+CMGD=%d", idx); | |
1849 | 2538 | if (err != AT_NOERROR) |
1850 | 2539 | RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
1851 | 2540 | else |
@@ -1921,20 +2610,20 @@ static void requestSmsStorageFull(void *data, size_t datalen, RIL_Token t) | ||
1921 | 2610 | * 1. memory was full and been cleaned up, inform modem memory is available now. |
1922 | 2611 | */ |
1923 | 2612 | switch (ack) { |
1924 | - case 0: | |
1925 | - /* Android will handle this, no need to inform modem. always return success. */ | |
1926 | - ALOGI("SMS storage full"); | |
1927 | - break; | |
1928 | - | |
1929 | - case 1: | |
1930 | - /* Since we are not using +CNMA command. It's fine to return without informing network */ | |
1931 | - ALOGI("Failed to inform network for Message Cleanup. Need cmd : ESMSMEMAVAIL"); | |
1932 | - break; | |
1933 | - | |
1934 | - default: | |
1935 | - ALOGE("%s() Invalid parameter", __func__); | |
1936 | - RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
1937 | - return; | |
2613 | + case 0: | |
2614 | + /* Android will handle this, no need to inform modem. always return success. */ | |
2615 | + ALOGI("SMS storage full"); | |
2616 | + break; | |
2617 | + | |
2618 | + case 1: | |
2619 | + /* Since we are not using +CNMA command. It's fine to return without informing network */ | |
2620 | + ALOGI("Failed to inform network for Message Cleanup. Need cmd : ESMSMEMAVAIL"); | |
2621 | + break; | |
2622 | + | |
2623 | + default: | |
2624 | + ALOGE("%s() Invalid parameter", __func__); | |
2625 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
2626 | + return; | |
1938 | 2627 | } |
1939 | 2628 | |
1940 | 2629 | RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
@@ -1982,7 +2671,7 @@ static void requestSMSAcknowledge(void *data, size_t datalen, RIL_Token t) | ||
1982 | 2671 | unsolResponse = RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT; |
1983 | 2672 | |
1984 | 2673 | RIL_onUnsolicitedResponse(unsolResponse, hpdu->sms_pdu, |
1985 | - strlen(hpdu->sms_pdu)); | |
2674 | + strlen(hpdu->sms_pdu)); | |
1986 | 2675 | |
1987 | 2676 | free(hpdu->sms_pdu); |
1988 | 2677 | free(hpdu); |
@@ -2262,234 +2951,246 @@ error: | ||
2262 | 2951 | * This is similar to the TS 27.007 "restricted SIM" operation |
2263 | 2952 | * where it assumes all of the EF selection will be done by the |
2264 | 2953 | * callee. |
2954 | + * | |
2955 | + * Reverse Engineered from Kuawei generic RIL * | |
2265 | 2956 | */ |
2266 | 2957 | |
2267 | - | |
2268 | -/* All files listed under ADF_USIM in 3GPP TS 31.102 */ | |
2269 | -static const int ef_usim_files[] = { | |
2270 | - 0x6F05, 0x6F06, 0x6F07, 0x6F08, 0x6F09, | |
2271 | - 0x6F2C, 0x6F31, 0x6F32, 0x6F37, 0x6F38, | |
2272 | - 0x6F39, 0x6F3B, 0x6F3C, 0x6F3E, 0x6F3F, | |
2273 | - 0x6F40, 0x6F41, 0x6F42, 0x6F43, 0x6F45, | |
2274 | - 0x6F46, 0x6F47, 0x6F48, 0x6F49, 0x6F4B, | |
2275 | - 0x6F4C, 0x6F4D, 0x6F4E, 0x6F4F, 0x6F50, | |
2276 | - 0x6F55, 0x6F56, 0x6F57, 0x6F58, 0x6F5B, | |
2277 | - 0x6F5C, 0x6F60, 0x6F61, 0x6F62, 0x6F73, | |
2278 | - 0x6F78, 0x6F7B, 0x6F7E, 0x6F80, 0x6F81, | |
2279 | - 0x6F82, 0x6F83, 0x6FAD, 0x6FB1, 0x6FB2, | |
2280 | - 0x6FB3, 0x6FB4, 0x6FB5, 0x6FB6, 0x6FB7, | |
2281 | - 0x6FC3, 0x6FC4, 0x6FC5, 0x6FC6, 0x6FC7, | |
2282 | - 0x6FC8, 0x6FC9, 0x6FCA, 0x6FCB, 0x6FCC, | |
2283 | - 0x6FCD, 0x6FCE, 0x6FCF, 0x6FD0, 0x6FD1, | |
2284 | - 0x6FD2, 0x6FD3, 0x6FD4, 0x6FD5, 0x6FD6, | |
2285 | - 0x6FD7, 0x6FD8, 0x6FD9, 0x6FDA, 0x6FDB, | |
2958 | +typedef struct _tagSW1SW2{ | |
2959 | + unsigned char mask[2]; | |
2960 | + unsigned char sw[2]; | |
2961 | + char *text; | |
2962 | +} SW1SW2; | |
2963 | + | |
2964 | +static const SW1SW2 sim_answ[]= | |
2965 | +{ | |
2966 | + /* Response to commands which are correctly executed */ | |
2967 | + { { 0xff, 0xff }, { 0x90, 0x00 } , "Ok" }, | |
2968 | + { { 0xff, 0x00 }, { 0x9f, 0x00 } , "%d response bytes available" }, | |
2969 | + /* Memory management */ | |
2970 | + { { 0xff, 0x00 }, { 0x92, 0x00 } , "Update successful but after using an internal retry of %d times" }, | |
2971 | + { { 0xff, 0xff }, { 0x92, 0x40 } , "memory problem" }, | |
2972 | + /* Referencing management */ | |
2973 | + { { 0xff, 0xff }, { 0x94, 0x00 } , "no EF selected" }, | |
2974 | + { { 0xff, 0xff }, { 0x94, 0x02 } , "out of range (invalid address)" }, | |
2975 | + { { 0xff, 0xff }, { 0x94, 0x04 } , "file ID or pattern not found" }, | |
2976 | + { { 0xff, 0xff }, { 0x94, 0x08 } , "file is inconsistent with the command" }, | |
2977 | + /* Security management */ | |
2978 | + { { 0xff, 0xff }, { 0x98, 0x02 } , "no CHV initialised" }, | |
2979 | + { { 0xff, 0xff }, { 0x98, 0x04 } , "access condition not fulfilled,authentication failed at least one attempt left" }, | |
2980 | + { { 0xff, 0xff }, { 0x98, 0x08 } , "in contradiction with CHV status" }, | |
2981 | + { { 0xff, 0xff }, { 0x98, 0x10 } , "in contradiction with invalidation stats" }, | |
2982 | + { { 0xff, 0xff }, { 0x98, 0x40 } , "unsuccessful CHV verification,no attempt left. CHV blocked" }, | |
2983 | + { { 0xff, 0xff }, { 0x98, 0x50 } , "increase cannot be performed,Max value reached" }, | |
2984 | + | |
2985 | + { { 0xff, 0x00 }, { 0x61, 0x00 } , "%d response bytes available" }, | |
2986 | + | |
2987 | + { { 0xff, 0xff }, { 0x62, 0x81 } , "returned data may be corrupt" }, | |
2988 | + { { 0xff, 0xff }, { 0x62, 0x82 } , "EOF reached prematurely" }, | |
2989 | + { { 0xff, 0xff }, { 0x62, 0x83 } , "selected file invalid" }, | |
2990 | + { { 0xff, 0xff }, { 0x62, 0x84 } , "FCI not formated" }, | |
2991 | + { { 0xff, 0x00 }, { 0x62, 0x00 } , "nvmem unchanged" }, | |
2992 | + | |
2993 | + { { 0xff, 0x00 }, { 0x63, 0x00 } , "nvmem changed" }, | |
2994 | + { { 0xff, 0x00 }, { 0x63, 0x81 } , "file filled up by last write" }, | |
2995 | + { { 0xff, 0xf0 }, { 0x63, 0xc0 } , "Counter=%1.1X" }, | |
2996 | + | |
2997 | + { { 0xff, 0xff }, { 0x64, 0x00 } , "nvmem unchanged" }, | |
2998 | + { { 0xff, 0x00 }, { 0x64, 0x00 } , "nvmem unchanged - RFU" }, | |
2999 | + | |
3000 | + { { 0xff, 0xff }, { 0x65, 0x00 } , "nvmem changed" }, | |
3001 | + { { 0xff, 0xff }, { 0x65, 0x81 } , "nvmem changed - memory failure" }, | |
3002 | + { { 0xff, 0x00 }, { 0x65, 0x00 } , "nvmem changed - unknown?" }, | |
3003 | + | |
3004 | + { { 0xff, 0x00 }, { 0x66, 0x00 } , "security related %d" }, | |
3005 | + | |
3006 | + { { 0xff, 0xff }, { 0x67, 0x00 } , "wrong length" }, | |
3007 | + { { 0xff, 0x00 }, { 0x67, 0x00 } , "wrong length - %d expected" }, | |
3008 | + | |
3009 | + { { 0xff, 0xff }, { 0x68, 0x81 } , "wrong cla - logical channel not supported" }, | |
3010 | + { { 0xff, 0xff }, { 0x68, 0x82 } , "wrong cla - secure messaging not supported" }, | |
3011 | + { { 0xff, 0x00 }, { 0x68, 0x00 } , "cla not supported" }, | |
3012 | + | |
3013 | + { { 0xff, 0xff }, { 0x69, 0x81 } , "command incompatible with file structure" }, | |
3014 | + { { 0xff, 0xff }, { 0x69, 0x82 } , "security status not satisfied" }, | |
3015 | + { { 0xff, 0xff }, { 0x69, 0x83 } , "authentication method blocked" }, | |
3016 | + { { 0xff, 0xff }, { 0x69, 0x84 } , "referenced data invalid" }, | |
3017 | + { { 0xff, 0xff }, { 0x69, 0x85 } , "conditions of use not satisfied" }, | |
3018 | + { { 0xff, 0xff }, { 0x69, 0x86 } , "command not allowed - no current EF" }, | |
3019 | + { { 0xff, 0xff }, { 0x69, 0x87 } , "expected SM data objects missing" }, | |
3020 | + { { 0xff, 0xff }, { 0x69, 0x88 } , "SM data objects incorrect" }, | |
3021 | + { { 0xff, 0x00 }, { 0x69, 0x00 } , "command not allowed" }, | |
3022 | + | |
3023 | + { { 0xff, 0xff }, { 0x6a, 0x80 } , "P1-P2: incorrect parameters in data field" }, | |
3024 | + { { 0xff, 0xff }, { 0x6a, 0x81 } , "P1-P2: function not supported" }, | |
3025 | + { { 0xff, 0xff }, { 0x6a, 0x82 } , "P1-P2: file not found" }, | |
3026 | + { { 0xff, 0xff }, { 0x6a, 0x83 } , "P1-P2: record not found" }, | |
3027 | + { { 0xff, 0xff }, { 0x6a, 0x84 } , "P1-P2: not enough memory space in file" }, | |
3028 | + { { 0xff, 0xff }, { 0x6a, 0x85 } , "P1-P2: Lc inconsistent with TLV" }, | |
3029 | + { { 0xff, 0xff }, { 0x6a, 0x86 } , "P1-P2 incorrect" }, | |
3030 | + { { 0xff, 0xff }, { 0x6a, 0x87 } , "P1-P2 inconsistent with Lc" }, | |
3031 | + { { 0xff, 0xff }, { 0x6a, 0x88 } , "Referenced data not found" }, | |
3032 | + { { 0xff, 0x00 }, { 0x6a, 0x00 } , "P1-P2 invalid" }, | |
3033 | + | |
3034 | + { { 0xff, 0x00 }, { 0x6b, 0x00 } , "P1-P2 invalid" }, | |
3035 | + | |
3036 | + { { 0xff, 0x00 }, { 0x6c, 0x00 } , "wrong length - %d expected" }, | |
3037 | + | |
3038 | + { { 0xff, 0x00 }, { 0x6d, 0x00 } , "INS code not supported or invalid" }, | |
3039 | + | |
3040 | + { { 0xff, 0x00 }, { 0x6e, 0x00 } , "CLA %02X not supported" }, | |
3041 | + | |
3042 | + { { 0xff, 0x00 }, { 0x6f, 0x00 } , "no precise diagnosis" }, | |
3043 | + | |
3044 | + { { 0x00, 0x00 }, { 0x00, 0x00 } , "Unknown response" } | |
2286 | 3045 | }; |
2287 | 3046 | |
2288 | -static const int ef_telecom_files[] = { | |
2289 | - 0x6F3A, 0x6F3D, 0x6F44, 0x6F4A, 0x6F54, | |
2290 | -}; | |
3047 | +/* Interpret and print SIM_IO command result */ | |
3048 | +static void print_simansw(unsigned char sw1, unsigned char sw2) | |
3049 | +{ | |
3050 | + unsigned j; | |
3051 | + | |
3052 | + ALOGD("sw1: 0x%02x, sw2: 0x%02x", sw1, sw2); | |
3053 | + for (j = 0; j < sizeof(sim_answ)/sizeof(sim_answ[0]); j++) { | |
3054 | + if ((sw1 & sim_answ[j].mask[0]) == sim_answ[j].sw[0] && | |
3055 | + (sw2 & sim_answ[j].mask[1]) == sim_answ[j].sw[1]) { | |
3056 | + ALOGD(sim_answ[j].text, sw2); | |
3057 | + return; | |
3058 | + } | |
3059 | + } | |
3060 | + | |
3061 | + ALOGD("Unknown error"); | |
3062 | +} | |
2291 | 3063 | |
2292 | -#define PATH_ADF_USIM_DIRECTORY "3F007FFF" | |
2293 | -#define PATH_ADF_TELECOM_DIRECTORY "3F007F10" | |
3064 | +static unsigned int hex2int(char dig) | |
3065 | +{ | |
3066 | + if (dig >= '0' && dig <= '9') | |
3067 | + return dig - '0'; | |
3068 | + if (dig >= 'a' && dig <= 'f') | |
3069 | + return dig - 'a' + 10; | |
3070 | + if (dig >= 'A' && dig <= 'F') | |
3071 | + return dig - 'A' + 10; | |
3072 | + return 0; | |
3073 | +} | |
2294 | 3074 | |
2295 | -/* RID: A000000087 = 3GPP, PIX: 1002 = 3GPP USIM */ | |
2296 | -#define USIM_APPLICATION_ID "A0000000871002" | |
3075 | +static void hexStringToBytes(unsigned char* b, const char* str) | |
3076 | +{ | |
3077 | + int len = strlen(str); | |
3078 | + len >>= 1; | |
3079 | + while (len--) { | |
3080 | + *b++ = (hex2int(str[0]) << 4) | hex2int(str[1]); | |
3081 | + str += 2; | |
3082 | + }; | |
3083 | +} | |
2297 | 3084 | |
2298 | -static int sendSimIOCmdICC(const RIL_SIM_IO_v6 *ioargs, ATResponse **atResponse, RIL_SIM_IO_Response *sr) | |
3085 | +static void requestSIM_IO(void *data, size_t datalen, RIL_Token t) | |
2299 | 3086 | { |
3087 | + ATResponse *atResponse = NULL; | |
3088 | + RIL_SIM_IO_Response sr; | |
2300 | 3089 | int err; |
2301 | - char *fmt; | |
2302 | - char *arg6; | |
2303 | - char *arg7; | |
3090 | + RIL_SIM_IO_v6 *p_args; | |
2304 | 3091 | char *line; |
3092 | + char *fmt; | |
3093 | + | |
3094 | + /* FIXME handle pin2 */ | |
3095 | + memset(&sr, 0, sizeof(sr)); | |
2305 | 3096 | |
2306 | - /* FIXME Handle pin2. */ | |
2307 | - memset(sr, 0, sizeof(*sr)); | |
3097 | + p_args = (RIL_SIM_IO_v6 *)data; | |
2308 | 3098 | |
2309 | - arg6 = ioargs->data; | |
2310 | - arg7 = ioargs->path; | |
3099 | + if (!p_args) | |
3100 | + goto error; | |
3101 | + | |
3102 | + if (p_args->path != NULL && (strlen(p_args->path) & 3) != 0) | |
3103 | + goto error; | |
2311 | 3104 | |
2312 | - if (arg7 && arg6) { | |
2313 | - fmt = "AT+CRSM=%d,%d,%d,%d,%d,\"%s\",\"%s\""; | |
2314 | - } else if (arg7) { | |
2315 | - fmt = "AT+CRSM=%d,%d,%d,%d,%d,,\"%s\""; | |
2316 | - arg6 = arg7; | |
2317 | - } else if (arg6) { | |
2318 | - fmt = "AT+CRSM=%d,%d,%d,%d,%d,\"%s\""; | |
3105 | + if (p_args->fileid == 0x4F30) { | |
3106 | + if (!p_args->data) { | |
3107 | + fmt = "AT+CRSM=%d,%d,%d,%d,%d,,\"3F007F105F3A\""; | |
3108 | + } else { | |
3109 | + fmt = "AT+CRSM=%d,%d,%d,%d,%d,%s,\"3F007F105F3A\""; | |
3110 | + } | |
2319 | 3111 | } else { |
2320 | - fmt = "AT+CRSM=%d,%d,%d,%d,%d"; | |
3112 | + if (!p_args->data) { | |
3113 | + fmt = "AT+CRSM=%d,%d,%d,%d,%d"; | |
3114 | + } else { | |
3115 | + fmt = "AT+CRSM=%d,%d,%d,%d,%d,%s"; | |
3116 | + } | |
2321 | 3117 | } |
2322 | 3118 | |
2323 | - err = at_send_command_singleline(fmt, "+CRSM:", atResponse,ioargs->command, | |
2324 | - ioargs->fileid, ioargs->p1, | |
2325 | - ioargs->p2, ioargs->p3, | |
2326 | - arg6, arg7); | |
3119 | + err = at_send_command_singleline(fmt, "+CRSM:", &atResponse, | |
3120 | + p_args->command, | |
3121 | + p_args->fileid, p_args->p1, | |
3122 | + p_args->p2, p_args->p3, | |
3123 | + p_args->data); | |
2327 | 3124 | |
2328 | 3125 | if (err != AT_NOERROR) |
2329 | - return err; | |
3126 | + goto error; | |
2330 | 3127 | |
2331 | - line = (*atResponse)->p_intermediates->line; | |
3128 | + line = atResponse->p_intermediates->line; | |
2332 | 3129 | |
2333 | 3130 | err = at_tok_start(&line); |
2334 | - if (err < 0) | |
2335 | - goto finally; | |
3131 | + if (err < 0) goto error; | |
2336 | 3132 | |
2337 | - err = at_tok_nextint(&line, &(sr->sw1)); | |
2338 | - if (err < 0) | |
2339 | - goto finally; | |
3133 | + err = at_tok_nextint(&line, &(sr.sw1)); | |
3134 | + if (err < 0) goto error; | |
2340 | 3135 | |
2341 | - err = at_tok_nextint(&line, &(sr->sw2)); | |
2342 | - if (err < 0) | |
2343 | - goto finally; | |
3136 | + err = at_tok_nextint(&line, &(sr.sw2)); | |
3137 | + if (err < 0) goto error; | |
2344 | 3138 | |
2345 | 3139 | if (at_tok_hasmore(&line)) { |
2346 | - err = at_tok_nextstr(&line, &(sr->simResponse)); | |
2347 | - if (err < 0) | |
2348 | - goto finally; | |
2349 | - } | |
2350 | - | |
2351 | -finally: | |
2352 | - return err; | |
2353 | -} | |
2354 | - | |
2355 | -static int convertSimIoFcp(RIL_SIM_IO_Response *sr, char **cvt) | |
2356 | -{ | |
2357 | - int err; | |
2358 | - /* size_t pos; */ | |
2359 | - size_t fcplen; | |
2360 | - struct ts_51011_921_resp resp; | |
2361 | - void *cvt_buf = NULL; | |
2362 | - | |
2363 | - if (!sr->simResponse || !cvt) { | |
2364 | - err = -EINVAL; | |
2365 | - goto error; | |
2366 | - } | |
2367 | - | |
2368 | - fcplen = strlen(sr->simResponse); | |
2369 | - if ((fcplen == 0) || (fcplen & 1)) { | |
2370 | - err = -EINVAL; | |
2371 | - goto error; | |
2372 | - } | |
2373 | - | |
2374 | - err = fcp_to_ts_51011(sr->simResponse, fcplen, &resp); | |
2375 | - if (err < 0) | |
2376 | - goto error; | |
2377 | - | |
2378 | - cvt_buf = malloc(sizeof(resp) * 2 + 1); | |
2379 | - if (!cvt_buf) { | |
2380 | - err = -ENOMEM; | |
2381 | - goto error; | |
3140 | + err = at_tok_nextstr(&line, &(sr.simResponse)); | |
3141 | + if (err < 0) goto error; | |
2382 | 3142 | } |
2383 | 3143 | |
2384 | - err = binaryToString((unsigned char*)(&resp), | |
2385 | - sizeof(resp), cvt_buf); | |
2386 | - if (err < 0) | |
2387 | - goto error; | |
2388 | - | |
2389 | - /* cvt_buf ownership is moved to the caller */ | |
2390 | - *cvt = cvt_buf; | |
2391 | - cvt_buf = NULL; | |
2392 | - | |
2393 | -finally: | |
2394 | - return err; | |
2395 | - | |
2396 | -error: | |
2397 | - free(cvt_buf); | |
2398 | - goto finally; | |
2399 | -} | |
3144 | + /* Interpret and print results as a debugging aid */ | |
3145 | + print_simansw(sr.sw1,sr.sw2); | |
2400 | 3146 | |
2401 | -/** | |
2402 | - * RIL_REQUEST_SIM_IO | |
2403 | - * | |
2404 | - * Request SIM I/O operation. | |
2405 | - * This is similar to the TS 27.007 "restricted SIM" operation | |
2406 | - * where it assumes all of the EF selection will be done by the | |
2407 | - * callee. | |
2408 | - */ | |
2409 | -static void requestSIM_IO(void *data, size_t datalen, RIL_Token t) | |
2410 | -{ | |
2411 | - (void) datalen; | |
2412 | - ATResponse *atResponse = NULL; | |
2413 | - RIL_SIM_IO_Response sr; | |
2414 | - int cvt_done = 0; | |
2415 | - int err; | |
2416 | - UICC_Type UiccType = getUICCType(); | |
3147 | + /* If dealing with a USIM card ... */ | |
3148 | + if (p_args->command == 0xC0 && | |
3149 | + sr.simResponse[0] == '6' && | |
3150 | + sr.simResponse[1] == '2' && | |
3151 | + sr.simResponse[6] == '0' && | |
3152 | + sr.simResponse[7] == '5' && | |
3153 | + strlen(sr.simResponse) <= 30 && | |
3154 | + getUICCType() != UICC_TYPE_SIM) { | |
2417 | 3155 | |
2418 | - int pathReplaced = 0; | |
2419 | - RIL_SIM_IO_v6 ioargsDup; | |
3156 | + /* Convert it to a format Android understands */ | |
2420 | 3157 | |
2421 | - /* | |
2422 | - * Android telephony framework does not support USIM cards properly, | |
2423 | - * send GSM filepath where as active cardtype is USIM. | |
2424 | - * Android RIL needs to change the file path of files listed under ADF-USIM | |
2425 | - * if current active cardtype is USIM | |
2426 | - */ | |
2427 | - memcpy(&ioargsDup, data, sizeof(RIL_SIM_IO_v6)); | |
2428 | - if (UICC_TYPE_USIM == UiccType) { | |
2429 | - unsigned int i; | |
2430 | - int err; | |
2431 | - unsigned int count = sizeof(ef_usim_files) / sizeof(int); | |
2432 | - | |
2433 | - for (i = 0; i < count; i++) { | |
2434 | - if (ef_usim_files[i] == ioargsDup.fileid) { | |
2435 | - err = asprintf(&ioargsDup.path, PATH_ADF_USIM_DIRECTORY); | |
2436 | - if (err < 0) | |
2437 | - goto error; | |
2438 | - pathReplaced = 1; | |
2439 | - ALOGD("%s() Path replaced for USIM: %d", __func__, ioargsDup.fileid); | |
2440 | - break; | |
2441 | - } | |
2442 | - } | |
2443 | - if(!pathReplaced){ | |
2444 | - unsigned int count2 = sizeof(ef_telecom_files) / sizeof(int); | |
2445 | - for (i = 0; i < count2; i++) { | |
2446 | - if (ef_telecom_files[i] == ioargsDup.fileid) { | |
2447 | - err = asprintf(&ioargsDup.path, PATH_ADF_TELECOM_DIRECTORY); | |
2448 | - if (err < 0) | |
2449 | - goto error; | |
2450 | - pathReplaced = 1; | |
2451 | - ALOGD("%s() Path replaced for telecom: %d", __func__, ioargsDup.fileid); | |
2452 | - break; | |
2453 | - } | |
2454 | - } | |
2455 | - } | |
2456 | - } | |
3158 | + unsigned char buf[15]; | |
3159 | + unsigned int val; | |
2457 | 3160 | |
2458 | - memset(&sr, 0, sizeof(sr)); | |
3161 | + // Convert to bytes... | |
3162 | + hexStringToBytes(buf,sr.simResponse); | |
2459 | 3163 | |
2460 | - err = sendSimIOCmdICC(&ioargsDup, &atResponse, &sr); | |
3164 | + // Reformat response... | |
3165 | + sr.simResponse[0x00] = '0'; | |
3166 | + sr.simResponse[0x01] = '0'; | |
3167 | + sr.simResponse[0x02] = '0'; | |
3168 | + sr.simResponse[0x03] = '0'; | |
2461 | 3169 | |
2462 | - if (err < 0) | |
2463 | - goto error; | |
3170 | + val = buf[8] * (buf[7] + (buf[6]<<8U)); | |
3171 | + sprintf(&sr.simResponse[0x04],"%04x",val & 0xFFFFU); | |
2464 | 3172 | |
2465 | - /* | |
2466 | - * In case the command is GET_RESPONSE and cardtype is 3G SIM | |
2467 | - * convert to 2G FCP | |
2468 | - */ | |
2469 | - if (ioargsDup.command == 0xC0 && UiccType != UICC_TYPE_SIM) { | |
2470 | - err = convertSimIoFcp(&sr, &sr.simResponse); | |
2471 | - if (err < 0) | |
2472 | - goto error; | |
2473 | - cvt_done = 1; /* sr.simResponse needs to be freed */ | |
3173 | + sr.simResponse[0x08] = sr.simResponse[0x16]; | |
3174 | + sr.simResponse[0x09] = sr.simResponse[0x17]; | |
3175 | + sr.simResponse[0x0A] = sr.simResponse[0x18]; | |
3176 | + sr.simResponse[0x0B] = sr.simResponse[0x19]; | |
3177 | + sr.simResponse[0x0D] = '4'; | |
3178 | + sr.simResponse[0x1A] = '0'; | |
3179 | + sr.simResponse[0x1B] = '1'; | |
3180 | + sr.simResponse[0x1C] = sr.simResponse[0x0E]; | |
3181 | + sr.simResponse[0x1D] = sr.simResponse[0x0F]; | |
3182 | + sr.simResponse[0x1E] = 0; | |
2474 | 3183 | } |
2475 | 3184 | |
2476 | 3185 | RIL_onRequestComplete(t, RIL_E_SUCCESS, &sr, sizeof(sr)); |
2477 | - | |
2478 | -finally: | |
2479 | 3186 | at_response_free(atResponse); |
2480 | - if (cvt_done) | |
2481 | - free(sr.simResponse); | |
2482 | - | |
2483 | - if (pathReplaced) | |
2484 | - free(ioargsDup.path); | |
2485 | 3187 | return; |
2486 | 3188 | |
2487 | 3189 | error: |
2488 | 3190 | RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
2489 | - goto finally; | |
3191 | + at_response_free(atResponse); | |
2490 | 3192 | } |
2491 | 3193 | |
2492 | - | |
2493 | 3194 | /* |
2494 | 3195 | * The following list contains values for the structure "RIL_AppStatus" to be |
2495 | 3196 | * sent to Android on a given SIM state. It is indexed by the SIM_Status above. |
@@ -2818,6 +3519,7 @@ static void requestQueryFacilityLock(void *data, size_t datalen, RIL_Token t) | ||
2818 | 3519 | char *facility_string = NULL; |
2819 | 3520 | char *facility_password = NULL; |
2820 | 3521 | char *facility_class = NULL; |
3522 | + const char *fmt; | |
2821 | 3523 | |
2822 | 3524 | (void) datalen; |
2823 | 3525 |
@@ -2830,7 +3532,16 @@ static void requestQueryFacilityLock(void *data, size_t datalen, RIL_Token t) | ||
2830 | 3532 | facility_password = ((char **) data)[1]; |
2831 | 3533 | facility_class = ((char **) data)[2]; |
2832 | 3534 | |
2833 | - err = at_send_command_singleline("AT+CLCK=\"%s\",2,%s,%s", "+CLCK:", &atResponse, | |
3535 | + if (facility_password != NULL && facility_password[0] != 0) { | |
3536 | + if (facility_class != NULL && facility_class[0] != 0) { | |
3537 | + fmt = "AT+CLCK=\"%s\",2,%s,%s"; | |
3538 | + } else { | |
3539 | + fmt = "AT+CLCK=\"%s\",2,%s"; | |
3540 | + } | |
3541 | + } else { | |
3542 | + fmt = "AT+CLCK=\"%s\",2"; | |
3543 | + } | |
3544 | + err = at_send_command_singleline(fmt, "+CLCK:", &atResponse, | |
2834 | 3545 | facility_string, facility_password, facility_class); |
2835 | 3546 | if (err != AT_NOERROR) |
2836 | 3547 | goto error; |
@@ -2896,48 +3607,48 @@ static void requestSetFacilityLock(void *data, size_t datalen, RIL_Token t) | ||
2896 | 3607 | * printing NULL with %s will give string "(null)". |
2897 | 3608 | */ |
2898 | 3609 | err = at_send_command("AT+CLCK=\"%s\",%d,\"%s\",%s", facility_string, |
2899 | - facility_mode, facility_password, facility_class); | |
3610 | + facility_mode, facility_password ? facility_password : "", facility_class); | |
2900 | 3611 | |
2901 | 3612 | if (at_get_error_type(err) == AT_ERROR) |
2902 | 3613 | goto exit; |
2903 | 3614 | if (err != AT_NOERROR) { |
2904 | 3615 | switch (at_get_cme_error(err)) { |
2905 | - /* CME ERROR 11: "SIM PIN required" happens when PIN is wrong */ | |
2906 | - case CME_SIM_PIN_REQUIRED: | |
2907 | - ALOGI("Wrong PIN"); | |
2908 | - errorril = RIL_E_PASSWORD_INCORRECT; | |
2909 | - break; | |
2910 | - /* | |
2911 | - * CME ERROR 12: "SIM PUK required" happens when wrong PIN is used | |
2912 | - * 3 times in a row | |
2913 | - */ | |
2914 | - case CME_SIM_PUK_REQUIRED: | |
2915 | - ALOGI("PIN locked, change PIN with PUK"); | |
2916 | - num_retries = 0;/* PUK required */ | |
2917 | - errorril = RIL_E_PASSWORD_INCORRECT; | |
2918 | - break; | |
2919 | - /* CME ERROR 16: "Incorrect password" happens when PIN is wrong */ | |
2920 | - case CME_INCORRECT_PASSWORD: | |
2921 | - ALOGI("Incorrect password, Facility: %s", facility_string); | |
2922 | - errorril = RIL_E_PASSWORD_INCORRECT; | |
2923 | - break; | |
2924 | - /* CME ERROR 17: "SIM PIN2 required" happens when PIN2 is wrong */ | |
2925 | - case CME_SIM_PIN2_REQUIRED: | |
2926 | - ALOGI("Wrong PIN2"); | |
2927 | - errorril = RIL_E_PASSWORD_INCORRECT; | |
2928 | - break; | |
2929 | - /* | |
2930 | - * CME ERROR 18: "SIM PUK2 required" happens when wrong PIN2 is used | |
2931 | - * 3 times in a row | |
2932 | - */ | |
2933 | - case CME_SIM_PUK2_REQUIRED: | |
2934 | - ALOGI("PIN2 locked, change PIN2 with PUK2"); | |
2935 | - num_retries = 0;/* PUK2 required */ | |
2936 | - errorril = RIL_E_SIM_PUK2; | |
2937 | - break; | |
2938 | - default: /* some other error */ | |
2939 | - num_retries = -1; | |
2940 | - break; | |
3616 | + /* CME ERROR 11: "SIM PIN required" happens when PIN is wrong */ | |
3617 | + case CME_SIM_PIN_REQUIRED: | |
3618 | + ALOGI("Wrong PIN"); | |
3619 | + errorril = RIL_E_PASSWORD_INCORRECT; | |
3620 | + break; | |
3621 | + /* | |
3622 | + * CME ERROR 12: "SIM PUK required" happens when wrong PIN is used | |
3623 | + * 3 times in a row | |
3624 | + */ | |
3625 | + case CME_SIM_PUK_REQUIRED: | |
3626 | + ALOGI("PIN locked, change PIN with PUK"); | |
3627 | + num_retries = 0;/* PUK required */ | |
3628 | + errorril = RIL_E_PASSWORD_INCORRECT; | |
3629 | + break; | |
3630 | + /* CME ERROR 16: "Incorrect password" happens when PIN is wrong */ | |
3631 | + case CME_INCORRECT_PASSWORD: | |
3632 | + ALOGI("Incorrect password, Facility: %s", facility_string); | |
3633 | + errorril = RIL_E_PASSWORD_INCORRECT; | |
3634 | + break; | |
3635 | + /* CME ERROR 17: "SIM PIN2 required" happens when PIN2 is wrong */ | |
3636 | + case CME_SIM_PIN2_REQUIRED: | |
3637 | + ALOGI("Wrong PIN2"); | |
3638 | + errorril = RIL_E_PASSWORD_INCORRECT; | |
3639 | + break; | |
3640 | + /* | |
3641 | + * CME ERROR 18: "SIM PUK2 required" happens when wrong PIN2 is used | |
3642 | + * 3 times in a row | |
3643 | + */ | |
3644 | + case CME_SIM_PUK2_REQUIRED: | |
3645 | + ALOGI("PIN2 locked, change PIN2 with PUK2"); | |
3646 | + num_retries = 0;/* PUK2 required */ | |
3647 | + errorril = RIL_E_SIM_PUK2; | |
3648 | + break; | |
3649 | + default: /* some other error */ | |
3650 | + num_retries = -1; | |
3651 | + break; | |
2941 | 3652 | } |
2942 | 3653 | goto finally; |
2943 | 3654 | } |
@@ -2981,7 +3692,7 @@ static void requestEnterSimPin(void *data, size_t datalen, RIL_Token t, int requ | ||
2981 | 3692 | err = at_send_command("AT+CPIN=\"%s\",\"%s\"", strings[0], strings[1]); |
2982 | 3693 | } |
2983 | 3694 | } else if (datalen == 3 * sizeof(char *)) { |
2984 | - err = at_send_command("AT+CPIN=\"%s\",\"%s\"", strings[0], strings[1]); | |
3695 | + err = at_send_command("AT+CPIN=\"%s\",\"%s\"", strings[0], strings[1]); | |
2985 | 3696 | } else { |
2986 | 3697 | goto error; |
2987 | 3698 | } |
@@ -2990,25 +3701,30 @@ static void requestEnterSimPin(void *data, size_t datalen, RIL_Token t, int requ | ||
2990 | 3701 | |
2991 | 3702 | if (cme_err != CME_ERROR_NON_CME && err != AT_NOERROR) { |
2992 | 3703 | switch (cme_err) { |
2993 | - case CME_SIM_PIN_REQUIRED: | |
2994 | - case CME_SIM_PUK_REQUIRED: | |
2995 | - case CME_INCORRECT_PASSWORD: | |
2996 | - case CME_SIM_PIN2_REQUIRED: | |
2997 | - case CME_SIM_PUK2_REQUIRED: | |
2998 | - num_retries = 1; | |
2999 | - RIL_onRequestComplete(t, RIL_E_PASSWORD_INCORRECT, &num_retries, sizeof(int *)); | |
3000 | - break; | |
3001 | - default: | |
3002 | - goto error; | |
3704 | + case CME_SIM_PIN_REQUIRED: | |
3705 | + case CME_SIM_PUK_REQUIRED: | |
3706 | + case CME_INCORRECT_PASSWORD: | |
3707 | + case CME_SIM_PIN2_REQUIRED: | |
3708 | + case CME_SIM_PUK2_REQUIRED: | |
3709 | + num_retries = 1; | |
3710 | + RIL_onRequestComplete(t, RIL_E_PASSWORD_INCORRECT, &num_retries, sizeof(int *)); | |
3711 | + break; | |
3712 | + default: | |
3713 | + goto error; | |
3003 | 3714 | } |
3004 | 3715 | } else { |
3005 | - /* | |
3006 | - * Got OK, return success and wait for *EPEV to trigger poll | |
3007 | - * of SIM state. | |
3008 | - */ | |
3716 | + /* Got OK, return success. */ | |
3009 | 3717 | |
3010 | 3718 | num_retries = 1; |
3011 | 3719 | RIL_onRequestComplete(t, RIL_E_SUCCESS, &num_retries, sizeof(int *)); |
3720 | + | |
3721 | + /* Make sure we get notifications for network registeration | |
3722 | + of both voice and data now */ | |
3723 | + at_send_command("AT+CREG=2"); | |
3724 | + at_send_command("AT+CGREG=2"); | |
3725 | + | |
3726 | + /* Notify that SIM is ready */ | |
3727 | + setRadioState(RADIO_STATE_SIM_READY); | |
3012 | 3728 | } |
3013 | 3729 | return; |
3014 | 3730 | error: |
@@ -3124,15 +3840,15 @@ static void pollOperatorSelected(void *params) | ||
3124 | 3840 | Loop and try again. */ |
3125 | 3841 | if (!at_tok_hasmore(&line)) { |
3126 | 3842 | switch (s_registrationDeniedReason) { |
3127 | - case IMSI_UNKNOWN_IN_HLR: /* fall through */ | |
3128 | - case ILLEGAL_ME: | |
3129 | - RIL_onRequestComplete(t, RIL_E_ILLEGAL_SIM_OR_ME, NULL, 0); | |
3130 | - free(poll_params); | |
3131 | - break; | |
3132 | - default: | |
3133 | - poll_params->loopcount++; | |
3134 | - enqueueRILEvent(pollOperatorSelected, | |
3135 | - poll_params, &TIMEVAL_OPERATOR_SELECT_POLL); | |
3843 | + case IMSI_UNKNOWN_IN_HLR: /* fall through */ | |
3844 | + case ILLEGAL_ME: | |
3845 | + RIL_onRequestComplete(t, RIL_E_ILLEGAL_SIM_OR_ME, NULL, 0); | |
3846 | + free(poll_params); | |
3847 | + break; | |
3848 | + default: | |
3849 | + poll_params->loopcount++; | |
3850 | + enqueueRILEvent(pollOperatorSelected, | |
3851 | + poll_params, &TIMEVAL_OPERATOR_SELECT_POLL); | |
3136 | 3852 | } |
3137 | 3853 | } else { |
3138 | 3854 | /* We got operator, throw a success! */ |
@@ -3514,6 +4230,135 @@ error: | ||
3514 | 4230 | goto finally; |
3515 | 4231 | } |
3516 | 4232 | |
4233 | +/** | |
4234 | + * RIL_REQUEST_SET_BAND_MODE | |
4235 | + * | |
4236 | + * Assign a specified band for RF configuration. | |
4237 | + * | |
4238 | + * "data" is int * | |
4239 | + * ((int *)data)[0] is == 0 for "unspecified" (selected by baseband automatically) | |
4240 | + * ((int *)data)[0] is == 1 for "EURO band" (GSM-900 / DCS-1800 / WCDMA-IMT-2000) | |
4241 | + * ((int *)data)[0] is == 2 for "US band" (GSM-850 / PCS-1900 / WCDMA-850 / WCDMA-PCS-1900) | |
4242 | + * ((int *)data)[0] is == 3 for "JPN band" (WCDMA-800 / WCDMA-IMT-2000) | |
4243 | + * ((int *)data)[0] is == 4 for "AUS band" (GSM-900 / DCS-1800 / WCDMA-850 / WCDMA-IMT-2000) | |
4244 | + * ((int *)data)[0] is == 5 for "AUS band 2" (GSM-900 / DCS-1800 / WCDMA-850) | |
4245 | + * | |
4246 | + * "response" is NULL | |
4247 | + * | |
4248 | + * Valid errors: | |
4249 | + * SUCCESS | |
4250 | + * RADIO_NOT_AVAILABLE | |
4251 | + * GENERIC_FAILURE | |
4252 | + */ | |
4253 | +static void requestSetBandMode(void *data, size_t datalen, RIL_Token t) | |
4254 | +{ | |
4255 | + int band,err; | |
4256 | + const char* strband; | |
4257 | + | |
4258 | + assert (datalen >= sizeof(int *)); | |
4259 | + band = ((int *)data)[0]; | |
4260 | + | |
4261 | + switch (band) { | |
4262 | + case 0: | |
4263 | + default: | |
4264 | + strband = "3FFFFFFF"; | |
4265 | + break; | |
4266 | + case 1: | |
4267 | + strband = "2000000400380"; | |
4268 | + break; | |
4269 | + case 2: | |
4270 | + strband = "4A80000"; | |
4271 | + break; | |
4272 | + } | |
4273 | + | |
4274 | + /* Set allowed bands */ | |
4275 | + err = at_send_command("AT^SYSCFG=16,3,%s,1,2", strband ); | |
4276 | + if (err != AT_NOERROR) | |
4277 | + goto error; | |
4278 | + | |
4279 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, sizeof(int)); | |
4280 | + return; | |
4281 | + | |
4282 | +error: | |
4283 | + ALOGE("ERROR: requestSetBandMode() failed"); | |
4284 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
4285 | + | |
4286 | +} | |
4287 | + | |
4288 | +/** | |
4289 | + * RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE | |
4290 | + * | |
4291 | + * Query the list of band mode supported by RF. | |
4292 | + * | |
4293 | + * "data" is NULL | |
4294 | + * | |
4295 | + * "response" is int * | |
4296 | + * "response" points to an array of int's, the int[0] is the size of array, reset is one for | |
4297 | + * each available band mode. | |
4298 | + * | |
4299 | + * 0 for "unspecified" (selected by baseband automatically) | |
4300 | + * 1 for "EURO band" (GSM-900 / DCS-1800 / WCDMA-IMT-2000) | |
4301 | + * 2 for "US band" (GSM-850 / PCS-1900 / WCDMA-850 / WCDMA-PCS-1900) | |
4302 | + * 3 for "JPN band" (WCDMA-800 / WCDMA-IMT-2000) | |
4303 | + * 4 for "AUS band" (GSM-900 / DCS-1800 / WCDMA-850 / WCDMA-IMT-2000) | |
4304 | + * 5 for "AUS band 2" (GSM-900 / DCS-1800 / WCDMA-850) | |
4305 | + * | |
4306 | + * Valid errors: | |
4307 | + * SUCCESS | |
4308 | + * RADIO_NOT_AVAILABLE | |
4309 | + * GENERIC_FAILURE | |
4310 | + * | |
4311 | + * See also: RIL_REQUEST_SET_BAND_MODE | |
4312 | + */ | |
4313 | +static void requestQueryAvailableBandMode(void *data, size_t datalen, RIL_Token t) | |
4314 | +{ | |
4315 | + int err = 0; | |
4316 | + char *line; | |
4317 | + ATResponse *atResponse = NULL; | |
4318 | + char *strband; | |
4319 | + int mode, acqorder, band; | |
4320 | + | |
4321 | + err = at_send_command_singleline("AT^SYSCFG?", "^SYSCFG:", &atResponse); | |
4322 | + if (err != AT_NOERROR) | |
4323 | + goto error; | |
4324 | + | |
4325 | + line = atResponse->p_intermediates->line; | |
4326 | + | |
4327 | + err = at_tok_start(&line); | |
4328 | + if (err < 0) | |
4329 | + goto error; | |
4330 | + | |
4331 | + err = at_tok_nextint(&line, &mode); | |
4332 | + if (err < 0) | |
4333 | + goto error; | |
4334 | + | |
4335 | + err = at_tok_nextint(&line, &acqorder); | |
4336 | + if (err < 0) | |
4337 | + goto error; | |
4338 | + | |
4339 | + err = at_tok_nextstr(&line, &strband); | |
4340 | + if (err < 0) | |
4341 | + goto error; | |
4342 | + | |
4343 | + if (strcmp(strband, "2000000400380") == 0) | |
4344 | + band = 1; | |
4345 | + else if (strcmp(strband, "4A80000") == 0) | |
4346 | + band = 2; | |
4347 | + else | |
4348 | + band = 0; | |
4349 | + | |
4350 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, &band, sizeof(int)); | |
4351 | + | |
4352 | +finally: | |
4353 | + at_response_free(atResponse); | |
4354 | + return; | |
4355 | + | |
4356 | +error: | |
4357 | + ALOGE("%s() Failed to get current band mode", __func__); | |
4358 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
4359 | + goto finally; | |
4360 | +} | |
4361 | + | |
3517 | 4362 | |
3518 | 4363 | /** |
3519 | 4364 | * RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE |
@@ -3529,28 +4374,19 @@ static void requestSetPreferredNetworkType(void *data, size_t datalen, RIL_Token | ||
3529 | 4374 | assert (datalen >= sizeof(int *)); |
3530 | 4375 | rat = ((int *)data)[0]; |
3531 | 4376 | |
3532 | - /* Huawei specific | |
3533 | - AT^SYSCFG=2,1,3FFFFFFF,2,4 for GPRS/EDGE Preferred | |
3534 | - AT^SYSCFG=2,2,3FFFFFFF,2,4 for 3G Preferred | |
3535 | - AT^SYSCFG=13,1,3FFFFFFF,2,4 for GPRS/EDGE Only | |
3536 | - AT^SYSCFG=14,2,3FFFFFFF,2,4 for 3G Only | |
3537 | - | |
3538 | - The third parameter, 0x3FFFFFFF tells the card to use all bands. | |
3539 | - A value of 0x400380 here means GSM900/1800/WCDMA2100 only and a | |
3540 | - value of 0x200000 here means GSM1900 only. | |
3541 | - | |
3542 | - */ | |
3543 | - | |
3544 | 4377 | switch (rat) { |
3545 | - case PREF_NET_TYPE_GSM_ONLY: /* GSM only */ | |
3546 | - cmd = "AT^SYSCFG=13,1,3FFFFFFF,2,4"; /* for GPRS/EDGE Only */ | |
4378 | + case PREF_NET_TYPE_GSM_ONLY: /* GSM only */ | |
4379 | + cmd = "AT^SYSCFG=13,1,40000000,2,4"; /* GSM only */ | |
3547 | 4380 | break; |
3548 | - case PREF_NET_TYPE_GSM_WCDMA: /* WCDMA only */ | |
3549 | - cmd = "AT^SYSCFG=14,2,3FFFFFFF,2,4"; /* for 3G Only */ | |
4381 | + case PREF_NET_TYPE_WCDMA: /* WCDMA only */ | |
4382 | + cmd = "AT^SYSCFG=14,2,40000000,2,4"; /* WCDMA only */ | |
3550 | 4383 | break; |
3551 | - case PREF_NET_TYPE_GSM_WCDMA_AUTO: | |
4384 | + case PREF_NET_TYPE_GSM_WCDMA: /* GSM/WCDMA (WCDMA preferred) */ | |
4385 | + cmd = "AT^SYSCFG=2,2,40000000,2,4"; /* Automatic, prefer WCDMA */ | |
4386 | + break; | |
4387 | + case PREF_NET_TYPE_GSM_WCDMA_AUTO: /* GSM/WCDMA (auto mode, according to PRL) */ | |
3552 | 4388 | default: /* Dual Mode - WCDMA preferred*/ |
3553 | - cmd = "AT^SYSCFG=2,2,3FFFFFFF,2,4"; /* for 3G Preferred */ | |
4389 | + cmd = "AT^SYSCFG=2,0,40000000,2,4"; /* for 3G Preferred */ | |
3554 | 4390 | break; |
3555 | 4391 | } |
3556 | 4392 |
@@ -3582,13 +4418,6 @@ error: | ||
3582 | 4418 | */ |
3583 | 4419 | static void requestGetPreferredNetworkType(RIL_Token t) |
3584 | 4420 | { |
3585 | - /* | |
3586 | - AT^SYSCFG=2,1,3FFFFFFF,1,2 for GPRS/EDGE Preferred | |
3587 | - AT^SYSCFG=2,2,3FFFFFFF,1,2 for 3G Preferred | |
3588 | - AT^SYSCFG=13,1,3FFFFFFF,1,2 for GPRS/EDGE Only | |
3589 | - AT^SYSCFG=14,2,3FFFFFFF,1,2 for 3G Only | |
3590 | - */ | |
3591 | - | |
3592 | 4421 | ATResponse *atResponse = NULL; |
3593 | 4422 | int err; |
3594 | 4423 | char *line; |
@@ -3614,12 +4443,14 @@ static void requestGetPreferredNetworkType(RIL_Token t) | ||
3614 | 4443 | if (err < 0) goto error; |
3615 | 4444 | |
3616 | 4445 | /* Based on reported syscfg */ |
3617 | - if (ret1 == 13) { | |
4446 | + if (ret1 == 13 && ret2 == 1) { | |
3618 | 4447 | response = PREF_NET_TYPE_GSM_ONLY; /* GSM only */ |
3619 | - } else if (ret1 == 14) { | |
3620 | - response = PREF_NET_TYPE_GSM_WCDMA; /* WCDMA only */ | |
4448 | + } else if (ret1 == 14 && ret2 == 2) { | |
4449 | + response = PREF_NET_TYPE_WCDMA; /* WCDMA only */ | |
4450 | + } else if (ret1 == 2 && ret2 == 2) { | |
4451 | + response = PREF_NET_TYPE_GSM_WCDMA; /* GSM or WCDMA */ | |
3621 | 4452 | } else { |
3622 | - response = PREF_NET_TYPE_GSM_WCDMA_AUTO; /* for 3G Preferred */ | |
4453 | + response = PREF_NET_TYPE_GSM_WCDMA_AUTO; /* Auto */ | |
3623 | 4454 | } |
3624 | 4455 | |
3625 | 4456 | D("requestGetPreferredNetworkType() mode:%d", response); |
@@ -3660,25 +4491,33 @@ enum CREG_stat { | ||
3660 | 4491 | * |
3661 | 4492 | * Request current registration state. |
3662 | 4493 | */ |
3663 | -static void requestRegistrationState(RIL_Token t) | |
4494 | +static void requestRegistrationState(RIL_Token t, int data) | |
3664 | 4495 | { |
3665 | - int err = 0; | |
3666 | - const char resp_size = 15; | |
3667 | - int response[15]; | |
3668 | - char *responseStr[15] = {0}; | |
4496 | + const int resp_size = 15; | |
4497 | + int response[resp_size]; | |
4498 | + char *responseStr[resp_size]; | |
3669 | 4499 | ATResponse *cgreg_resp = NULL; |
3670 | 4500 | char *line; |
3671 | 4501 | int commas = 0; |
3672 | 4502 | int skip, cs_status = 0; |
3673 | 4503 | int i; |
4504 | + int err; | |
4505 | + const char *cmd; | |
4506 | + const char *prefix; | |
3674 | 4507 | |
3675 | 4508 | /* Setting default values in case values are not returned by AT command */ |
3676 | - for (i = 0; i < resp_size; i++) | |
3677 | - responseStr[i] = NULL; | |
3678 | - | |
4509 | + memset(responseStr, 0, sizeof(responseStr)); | |
3679 | 4510 | memset(response, 0, sizeof(response)); |
3680 | 4511 | |
3681 | - err = at_send_command_singleline("AT+CREG?", "+CREG:", &cgreg_resp); | |
4512 | + if (data == 0) { | |
4513 | + cmd = "AT+CREG?"; | |
4514 | + prefix = "+CREG:"; | |
4515 | + } else { | |
4516 | + cmd = "AT+CGREG?"; | |
4517 | + prefix = "+CGREG:"; | |
4518 | + } | |
4519 | + | |
4520 | + err = at_send_command_singleline(cmd, prefix, &cgreg_resp); | |
3682 | 4521 | if (err != AT_NOERROR) |
3683 | 4522 | goto error; |
3684 | 4523 |
@@ -3716,62 +4555,117 @@ static void requestRegistrationState(RIL_Token t) | ||
3716 | 4555 | goto error; |
3717 | 4556 | |
3718 | 4557 | switch (commas) { |
3719 | - case 0: /* +CREG: <stat> */ | |
3720 | - err = at_tok_nextint(&line, &response[0]); | |
3721 | - if (err < 0) | |
3722 | - goto error; | |
4558 | + case 0: /* +CREG: <stat> */ | |
4559 | + err = at_tok_nextint(&line, &response[0]); | |
4560 | + if (err < 0) | |
4561 | + goto error; | |
3723 | 4562 | |
3724 | - response[1] = -1; | |
3725 | - response[2] = -1; | |
3726 | - break; | |
4563 | + response[1] = -1; | |
4564 | + response[2] = -1; | |
4565 | + break; | |
3727 | 4566 | |
3728 | - case 1: /* +CREG: <n>, <stat> */ | |
3729 | - err = at_tok_nextint(&line, &skip); | |
3730 | - if (err < 0) | |
3731 | - goto error; | |
4567 | + case 1: /* +CREG: <n>, <stat> */ | |
4568 | + err = at_tok_nextint(&line, &skip); | |
4569 | + if (err < 0) | |
4570 | + goto error; | |
3732 | 4571 | |
3733 | - err = at_tok_nextint(&line, &response[0]); | |
3734 | - if (err < 0) | |
3735 | - goto error; | |
4572 | + err = at_tok_nextint(&line, &response[0]); | |
4573 | + if (err < 0) | |
4574 | + goto error; | |
3736 | 4575 | |
3737 | - response[1] = -1; | |
3738 | - response[2] = -1; | |
3739 | - if (err < 0) | |
3740 | - goto error; | |
3741 | - break; | |
3742 | - case 2: /* +CREG: <stat>, <lac>, <cid> */ | |
3743 | - err = at_tok_nextint(&line, &response[0]); | |
3744 | - if (err < 0) | |
3745 | - goto error; | |
4576 | + response[1] = -1; | |
4577 | + response[2] = -1; | |
4578 | + if (err < 0) | |
4579 | + goto error; | |
4580 | + break; | |
4581 | + case 2: /* +CREG: <stat>, <lac>, <cid> */ | |
4582 | + err = at_tok_nextint(&line, &response[0]); | |
4583 | + if (err < 0) | |
4584 | + goto error; | |
3746 | 4585 | |
3747 | - err = at_tok_nexthexint(&line, &response[1]); | |
3748 | - if (err < 0) | |
3749 | - goto error; | |
4586 | + err = at_tok_nexthexint(&line, &response[1]); | |
4587 | + if (err < 0) | |
4588 | + goto error; | |
3750 | 4589 | |
3751 | - err = at_tok_nexthexint(&line, &response[2]); | |
3752 | - if (err < 0) | |
3753 | - goto error; | |
3754 | - break; | |
3755 | - case 3: /* +CREG: <n>, <stat>, <lac>, <cid> */ | |
3756 | - case 4: /* +CREG: <n>, <stat>, <lac>, <cid>, <?> */ | |
3757 | - err = at_tok_nextint(&line, &skip); | |
3758 | - if (err < 0) | |
3759 | - goto error; | |
4590 | + err = at_tok_nexthexint(&line, &response[2]); | |
4591 | + if (err < 0) | |
4592 | + goto error; | |
4593 | + break; | |
4594 | + case 3: /* +CREG: <n>, <stat>, <lac>, <cid> */ | |
4595 | + case 4: /* +CREG: <n>, <stat>, <lac>, <cid>, <?> */ | |
4596 | + err = at_tok_nextint(&line, &skip); | |
4597 | + if (err < 0) | |
4598 | + goto error; | |
3760 | 4599 | |
3761 | - err = at_tok_nextint(&line, &response[0]); | |
3762 | - if (err < 0) | |
3763 | - goto error; | |
4600 | + err = at_tok_nextint(&line, &response[0]); | |
4601 | + if (err < 0) | |
4602 | + goto error; | |
3764 | 4603 | |
3765 | - err = at_tok_nexthexint(&line, &response[1]); | |
3766 | - if (err < 0) | |
3767 | - goto error; | |
4604 | + err = at_tok_nexthexint(&line, &response[1]); | |
4605 | + if (err < 0) | |
4606 | + goto error; | |
3768 | 4607 | |
3769 | - err = at_tok_nexthexint(&line, &response[2]); | |
3770 | - if (err < 0) | |
4608 | + err = at_tok_nexthexint(&line, &response[2]); | |
4609 | + | |
4610 | + /* Hack for broken +CGREG responses which don't return the network type */ | |
4611 | + ATResponse *p_response_op = NULL; | |
4612 | + err = at_send_command_singleline("AT+COPS?", "+COPS:", &p_response_op); | |
4613 | + /* We need to get the 4th return param */ | |
4614 | + int commas_op; | |
4615 | + commas_op = 0; | |
4616 | + char *p_op, *line_op; | |
4617 | + line_op = p_response_op->p_intermediates->line; | |
4618 | + | |
4619 | + for (p_op = line_op ; *p_op != '\0' ;p_op++) { | |
4620 | + if (*p_op == ',') commas_op++; | |
4621 | + } | |
4622 | + | |
4623 | + if (commas_op == 3) { | |
4624 | + err = at_tok_start(&line_op); | |
4625 | + err = at_tok_nextint(&line_op, &skip); | |
4626 | + if (err < 0) goto error; | |
4627 | + err = at_tok_nextint(&line_op, &skip); | |
4628 | + if (err < 0) goto error; | |
4629 | + err = at_tok_nextint(&line_op, &skip); | |
4630 | + if (err < 0) goto error; | |
4631 | + err = at_tok_nextint(&line_op, &response[3]); | |
4632 | + if (err < 0) goto error; | |
4633 | + /* Now translate to 'Broken Android Speak' - can't follow the GSM spec */ | |
4634 | + switch (response[3]) { | |
4635 | + /* GSM/GSM Compact - aka GRPS */ | |
4636 | + case 0: | |
4637 | + case 1: | |
4638 | + response[3] = 1; | |
4639 | + break; | |
4640 | + | |
4641 | + /* EGPRS - aka EDGE */ | |
4642 | + case 3: | |
4643 | + response[3] = 2; | |
4644 | + break; | |
4645 | + | |
4646 | + /* UTRAN - UMTS aka 3G */ | |
4647 | + case 2: | |
4648 | + case 7: | |
4649 | + response[3] = 3; | |
4650 | + break; | |
4651 | + | |
4652 | + /* UTRAN with HSDPA and/or HSUPA aka Turbo-3G*/ | |
4653 | + case 4: | |
4654 | + case 5: | |
4655 | + case 6: | |
4656 | + response[3] = 9; | |
4657 | + break; | |
4658 | + } | |
4659 | + } | |
4660 | + | |
4661 | + at_response_free(p_response_op); | |
4662 | + /* End hack */ | |
4663 | + | |
4664 | + if (err < 0) | |
4665 | + goto error; | |
4666 | + break; | |
4667 | + default: | |
3771 | 4668 | goto error; |
3772 | - break; | |
3773 | - default: | |
3774 | - goto error; | |
3775 | 4669 | } |
3776 | 4670 | |
3777 | 4671 |
@@ -3781,10 +4675,11 @@ static void requestRegistrationState(RIL_Token t) | ||
3781 | 4675 | asprintf(&responseStr[1], "%04x", response[1]); |
3782 | 4676 | if (response[2] > 0) |
3783 | 4677 | asprintf(&responseStr[2], "%08x", response[2]); |
4678 | + if (response[3] > 0) | |
4679 | + asprintf(&responseStr[3], "%d", response[3]); | |
3784 | 4680 | |
3785 | - if (response[0] == CGREG_STAT_REG_HOME_NET || | |
3786 | - response[0] == CGREG_STAT_ROAMING) | |
3787 | - responseStr[3] = (0); | |
4681 | + if (data == 1) | |
4682 | + responseStr[5] = (char*) "1"; | |
3788 | 4683 | |
3789 | 4684 | RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, |
3790 | 4685 | resp_size * sizeof(char *)); |
@@ -3803,171 +4698,6 @@ error: | ||
3803 | 4698 | goto finally; |
3804 | 4699 | } |
3805 | 4700 | |
3806 | - | |
3807 | -/** | |
3808 | - * RIL_REQUEST_DATA_REGISTRATION_STATE | |
3809 | - * | |
3810 | - * Request current GPRS registration state. | |
3811 | - */ | |
3812 | -static void requestGprsRegistrationState(RIL_Token t) | |
3813 | -{ | |
3814 | - int err = 0; | |
3815 | - const char resp_size = 6; | |
3816 | - int response[resp_size]; | |
3817 | - char *responseStr[resp_size]; | |
3818 | - ATResponse *atResponse = NULL; | |
3819 | - char *line, *p; | |
3820 | - int commas = 0; | |
3821 | - int skip, tmp; | |
3822 | - int count = 3; | |
3823 | - | |
3824 | - memset(responseStr, 0, sizeof(responseStr)); | |
3825 | - memset(response, 0, sizeof(response)); | |
3826 | - response[1] = -1; | |
3827 | - response[2] = -1; | |
3828 | - | |
3829 | - err = at_send_command_singleline("AT+CGREG?", "+CGREG: ", &atResponse); | |
3830 | - if (err != AT_NOERROR) | |
3831 | - goto error; | |
3832 | - | |
3833 | - line = atResponse->p_intermediates->line; | |
3834 | - err = at_tok_start(&line); | |
3835 | - if (err < 0) | |
3836 | - goto error; | |
3837 | - /* | |
3838 | - * The solicited version of the +CGREG response is | |
3839 | - * +CGREG: n, stat, [lac, cid [,<AcT>]] | |
3840 | - * and the unsolicited version is | |
3841 | - * +CGREG: stat, [lac, cid [,<AcT>]] | |
3842 | - * The <n> parameter is basically "is unsolicited creg on?" | |
3843 | - * which it should always be. | |
3844 | - * | |
3845 | - * Now we should normally get the solicited version here, | |
3846 | - * but the unsolicited version could have snuck in | |
3847 | - * so we have to handle both. | |
3848 | - * | |
3849 | - * Also since the LAC, CID and AcT are only reported when registered, | |
3850 | - * we can have 1, 2, 3, 4 or 5 arguments here. | |
3851 | - */ | |
3852 | - /* Count number of commas */ | |
3853 | - p = line; | |
3854 | - err = at_tok_charcounter(line, ',', &commas); | |
3855 | - if (err < 0) { | |
3856 | - ALOGE("%s() at_tok_charcounter failed", __func__); | |
3857 | - goto error; | |
3858 | - } | |
3859 | - | |
3860 | - switch (commas) { | |
3861 | - case 0: /* +CGREG: <stat> */ | |
3862 | - err = at_tok_nextint(&line, &response[0]); | |
3863 | - if (err < 0) goto error; | |
3864 | - break; | |
3865 | - | |
3866 | - case 1: /* +CGREG: <n>, <stat> */ | |
3867 | - err = at_tok_nextint(&line, &skip); | |
3868 | - if (err < 0) goto error; | |
3869 | - err = at_tok_nextint(&line, &response[0]); | |
3870 | - if (err < 0) goto error; | |
3871 | - break; | |
3872 | - | |
3873 | - case 2: /* +CGREG: <stat>, <lac>, <cid> */ | |
3874 | - err = at_tok_nextint(&line, &response[0]); | |
3875 | - if (err < 0) goto error; | |
3876 | - err = at_tok_nexthexint(&line, &response[1]); | |
3877 | - if (err < 0) goto error; | |
3878 | - err = at_tok_nexthexint(&line, &response[2]); | |
3879 | - if (err < 0) goto error; | |
3880 | - break; | |
3881 | - | |
3882 | - case 3: /* +CGREG: <n>, <stat>, <lac>, <cid> */ | |
3883 | - /* +CGREG: <stat>, <lac>, <cid>, <AcT> */ | |
3884 | - err = at_tok_nextint(&line, &tmp); | |
3885 | - if (err < 0) goto error; | |
3886 | - | |
3887 | - /* We need to check if the second parameter is <lac> */ | |
3888 | - if (*(line) == '"') { | |
3889 | - response[0] = tmp; /* <stat> */ | |
3890 | - err = at_tok_nexthexint(&line, &response[1]); /* <lac> */ | |
3891 | - if (err < 0) goto error; | |
3892 | - err = at_tok_nexthexint(&line, &response[2]); /* <cid> */ | |
3893 | - if (err < 0) goto error; | |
3894 | - err = at_tok_nextint(&line, &response[3]); /* <AcT> */ | |
3895 | - if (err < 0) goto error; | |
3896 | - count = 4; | |
3897 | - } else { | |
3898 | - err = at_tok_nextint(&line, &response[0]); /* <stat> */ | |
3899 | - if (err < 0) goto error; | |
3900 | - err = at_tok_nexthexint(&line, &response[1]); /* <lac> */ | |
3901 | - if (err < 0) goto error; | |
3902 | - err = at_tok_nexthexint(&line, &response[2]); /* <cid> */ | |
3903 | - if (err < 0) goto error; | |
3904 | - } | |
3905 | - break; | |
3906 | - | |
3907 | - case 4: /* +CGREG: <n>, <stat>, <lac>, <cid>, <AcT> */ | |
3908 | - err = at_tok_nextint(&line, &skip); /* <n> */ | |
3909 | - if (err < 0) goto error; | |
3910 | - err = at_tok_nextint(&line, &response[0]); /* <stat> */ | |
3911 | - if (err < 0) goto error; | |
3912 | - err = at_tok_nexthexint(&line, &response[1]); /* <lac> */ | |
3913 | - if (err < 0) goto error; | |
3914 | - err = at_tok_nexthexint(&line, &response[2]); /* <cid> */ | |
3915 | - if (err < 0) goto error; | |
3916 | - err = at_tok_nextint(&line, &response[3]); /* <AcT> */ | |
3917 | - if (err < 0) goto error; | |
3918 | - count = 4; | |
3919 | - break; | |
3920 | - | |
3921 | - default: | |
3922 | - ALOGE("%s() Invalid input", __func__); | |
3923 | - goto error; | |
3924 | - } | |
3925 | - | |
3926 | - /* Converting to stringlist for Android */ | |
3927 | - | |
3928 | - asprintf(&responseStr[0], "%d", response[0]); /* state */ | |
3929 | - | |
3930 | - if (response[1] >= 0) | |
3931 | - asprintf(&responseStr[1], "%04x", response[1]); /* LAC */ | |
3932 | - else | |
3933 | - responseStr[1] = NULL; | |
3934 | - | |
3935 | - if (response[2] >= 0) | |
3936 | - asprintf(&responseStr[2], "%08x", response[2]); /* CID */ | |
3937 | - else | |
3938 | - responseStr[2] = NULL; | |
3939 | - | |
3940 | - if (response[0] == CGREG_STAT_REG_HOME_NET || | |
3941 | - response[0] == CGREG_STAT_ROAMING) | |
3942 | - asprintf(&responseStr[3], "%d",response[3]); | |
3943 | - else | |
3944 | - responseStr[3] = NULL; | |
3945 | - | |
3946 | - responseStr[5] = (char*) "1"; | |
3947 | - | |
3948 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, resp_size * sizeof(char *)); | |
3949 | - | |
3950 | -finally: | |
3951 | - | |
3952 | - if (responseStr[0]) | |
3953 | - free(responseStr[0]); | |
3954 | - if (responseStr[1]) | |
3955 | - free(responseStr[1]); | |
3956 | - if (responseStr[2]) | |
3957 | - free(responseStr[2]); | |
3958 | - if (responseStr[3]) | |
3959 | - free(responseStr[3]); | |
3960 | - | |
3961 | - at_response_free(atResponse); | |
3962 | - return; | |
3963 | - | |
3964 | -error: | |
3965 | - ALOGE("%s Must never return an error when radio is on", __func__); | |
3966 | - RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
3967 | - goto finally; | |
3968 | -} | |
3969 | - | |
3970 | - | |
3971 | 4701 | /** |
3972 | 4702 | * RIL_REQUEST_OEM_HOOK_RAW |
3973 | 4703 | * |
@@ -4049,17 +4779,40 @@ static void unsolicitedNitzTime(const char * s) | ||
4049 | 4779 | { |
4050 | 4780 | int err; |
4051 | 4781 | char * response = NULL; |
4052 | - char * line = NULL; | |
4053 | - char * p = NULL; | |
4782 | + char * dt = NULL; | |
4783 | + char * tm = NULL; | |
4054 | 4784 | char * tz = NULL; /* Timezone */ |
4055 | 4785 | static char sNITZtime[64] = {0}; |
4056 | 4786 | |
4057 | - line = strdup(s); | |
4787 | + char * line = strdup(s); | |
4058 | 4788 | |
4059 | 4789 | /* Higher layers expect a NITZ string in this format: |
4060 | 4790 | * 08/10/28,19:08:37-20,1 (yy/mm/dd,hh:mm:ss(+/-)tz,dst) |
4061 | 4791 | */ |
4792 | + if (strStartsWith(s,"^NWTIME:")) { | |
4793 | + /* Network time, as reported by Huawei modems: | |
4794 | + ^NWTIME: 12/09/22,08:33:59+8,01 */ | |
4795 | + | |
4796 | + /* We got the network time, now assemble the response and send to upper layers */ | |
4797 | + at_tok_start(&line); | |
4798 | + | |
4799 | + err = at_tok_nextstr(&line, &dt); | |
4800 | + if (err < 0) goto error; | |
4801 | + | |
4802 | + err = at_tok_nextstr(&line, &tm); | |
4803 | + if (err < 0) goto error; | |
4804 | + | |
4805 | + err = at_tok_nextstr(&line, &tz); | |
4806 | + if (err < 0) goto error; | |
4807 | + | |
4808 | + asprintf(&response, "%s,%s,%s", dt ,tm ,tz); | |
4809 | + RIL_onUnsolicitedResponse(RIL_UNSOL_NITZ_TIME_RECEIVED, response, strlen(response)); | |
4810 | + free(response); | |
4811 | + | |
4812 | + free(line); | |
4813 | + return; | |
4062 | 4814 | |
4815 | + } else | |
4063 | 4816 | if (strStartsWith(s,"+CTZV:")){ |
4064 | 4817 | |
4065 | 4818 | /* Get Time and Timezone data and store in static variable. |
@@ -4098,6 +4851,147 @@ static void unsolicitedNitzTime(const char * s) | ||
4098 | 4851 | |
4099 | 4852 | error: |
4100 | 4853 | ALOGE("Invalid NITZ line %s", s); |
4854 | + free(line); | |
4855 | +} | |
4856 | + | |
4857 | +/* Unsolicited call start status */ | |
4858 | +static void unsolicitedCONN(const char * s) | |
4859 | +{ | |
4860 | + int err; | |
4861 | + int call_id; | |
4862 | + int type; | |
4863 | + char * line = strdup(s); | |
4864 | + | |
4865 | + err = at_tok_start(&line); | |
4866 | + if (err < 0) goto error; | |
4867 | + | |
4868 | + err = at_tok_nextint(&line, &call_id); | |
4869 | + if (err < 0) goto error; | |
4870 | + | |
4871 | + err = at_tok_nextint(&line, &type); | |
4872 | + if (err < 0) goto error; | |
4873 | + | |
4874 | + ALOGD("Call started: ID:%d, type:%d", call_id, type); | |
4875 | + | |
4876 | + /* Store last call index */ | |
4877 | + lastCallIdx = call_id; | |
4878 | + | |
4879 | + free(line); | |
4880 | + return; | |
4881 | + | |
4882 | +error: | |
4883 | + ALOGI("Error getting Call Start status"); | |
4884 | + free(line); | |
4885 | + return; | |
4886 | +} | |
4887 | + | |
4888 | +/* Unsolicited call start status */ | |
4889 | +static void unsolicitedORIG(const char * s) | |
4890 | +{ | |
4891 | + int err; | |
4892 | + int call_id; | |
4893 | + int type; | |
4894 | + char * line = strdup(s); | |
4895 | + | |
4896 | + err = at_tok_start(&line); | |
4897 | + if (err < 0) goto error; | |
4898 | + | |
4899 | + err = at_tok_nextint(&line, &call_id); | |
4900 | + if (err < 0) goto error; | |
4901 | + | |
4902 | + err = at_tok_nextint(&line, &type); | |
4903 | + if (err < 0) goto error; | |
4904 | + | |
4905 | + ALOGD("Call originated: ID:%d, type:%d", call_id, type); | |
4906 | + | |
4907 | + /* Store last call index */ | |
4908 | + lastCallIdx = call_id; | |
4909 | + | |
4910 | + free(line); | |
4911 | + return; | |
4912 | + | |
4913 | +error: | |
4914 | + ALOGI("Error getting Call Originated status"); | |
4915 | + free(line); | |
4916 | + return; | |
4917 | +} | |
4918 | + | |
4919 | +/* Unsolicited call conference status */ | |
4920 | +static void unsolicitedCONF(const char * s) | |
4921 | +{ | |
4922 | + int err; | |
4923 | + int call_id; | |
4924 | + char * line = strdup(s); | |
4925 | + | |
4926 | + err = at_tok_start(&line); | |
4927 | + if (err < 0) goto error; | |
4928 | + | |
4929 | + err = at_tok_nextint(&line, &call_id); | |
4930 | + if (err < 0) goto error; | |
4931 | + | |
4932 | + ALOGD("Call conference started: ID:%d", call_id); | |
4933 | + | |
4934 | + /* Store last call index */ | |
4935 | + lastCallIdx = call_id; | |
4936 | + | |
4937 | + free(line); | |
4938 | + return; | |
4939 | + | |
4940 | +error: | |
4941 | + ALOGI("Error getting Call Conference Start status"); | |
4942 | + free(line); | |
4943 | + return; | |
4944 | +} | |
4945 | + | |
4946 | +/* Unsolicited call end status */ | |
4947 | +static void unsolicitedCEND(const char * s) | |
4948 | +{ | |
4949 | + int err; | |
4950 | + int call_id; | |
4951 | + int duration; | |
4952 | + int end_status; | |
4953 | + int cc_cause = CALL_FAIL_NORMAL; | |
4954 | + char * line = strdup(s); | |
4955 | + | |
4956 | + err = at_tok_start(&line); | |
4957 | + if (err < 0) goto error; | |
4958 | + | |
4959 | + err = at_tok_nextint(&line, &call_id); | |
4960 | + if (err < 0) goto error; | |
4961 | + | |
4962 | + err = at_tok_nextint(&line, &duration); | |
4963 | + if (err < 0) goto error; | |
4964 | + | |
4965 | + err = at_tok_nextint(&line, &end_status); | |
4966 | + if (err < 0) goto error; | |
4967 | + | |
4968 | + if (at_tok_hasmore(&line)) { /* not present if error */ | |
4969 | + err = at_tok_nextint(&line, &cc_cause); | |
4970 | + if (err < 0) goto error; | |
4971 | + } | |
4972 | + | |
4973 | + ALOGD("Call ended: ID:%d, duration:%ds status:%d, cccause:%d", call_id, duration, end_status, cc_cause); | |
4974 | + | |
4975 | + /* Store last call error */ | |
4976 | + lastCallFailCause = cc_cause; | |
4977 | + | |
4978 | + free(line); | |
4979 | + return; | |
4980 | + | |
4981 | +error: | |
4982 | + ALOGI("Error getting Call End status"); | |
4983 | + lastCallFailCause = CALL_FAIL_NORMAL; | |
4984 | + free(line); | |
4985 | + return; | |
4986 | +} | |
4987 | + | |
4988 | + | |
4989 | +static void unsolicitedDCONN(const char *s) | |
4990 | +{ | |
4991 | +} | |
4992 | + | |
4993 | +static void unsolicitedDEND(const char *s) | |
4994 | +{ | |
4101 | 4995 | } |
4102 | 4996 | |
4103 | 4997 | static void unsolicitedRSSI(const char * s) |
@@ -4105,9 +4999,10 @@ static void unsolicitedRSSI(const char * s) | ||
4105 | 4999 | int err; |
4106 | 5000 | int rssi; |
4107 | 5001 | RIL_SignalStrength_v6 signalStrength; |
4108 | - char * line = NULL; | |
4109 | 5002 | |
4110 | - line = strdup(s); | |
5003 | + char * line = strdup(s); | |
5004 | + | |
5005 | + memset(&signalStrength, 0, sizeof(RIL_SignalStrength_v6)); | |
4111 | 5006 | |
4112 | 5007 | err = at_tok_start(&line); |
4113 | 5008 | if (err < 0) goto error; |
@@ -4115,22 +5010,14 @@ static void unsolicitedRSSI(const char * s) | ||
4115 | 5010 | err = at_tok_nextint(&line, &rssi); |
4116 | 5011 | if (err < 0) goto error; |
4117 | 5012 | |
5013 | + if (rssi == 99) return; | |
5014 | + | |
4118 | 5015 | signalStrength.GW_SignalStrength.signalStrength = rssi; |
4119 | 5016 | signalStrength.GW_SignalStrength.bitErrorRate = 99; |
4120 | - signalStrength.CDMA_SignalStrength.dbm = 0; | |
4121 | - signalStrength.CDMA_SignalStrength.ecio = 0; | |
4122 | - signalStrength.EVDO_SignalStrength.dbm = 0; | |
4123 | - signalStrength.EVDO_SignalStrength.ecio = 0; | |
4124 | - signalStrength.EVDO_SignalStrength.signalNoiseRatio = 0; | |
4125 | - signalStrength.LTE_SignalStrength.signalStrength = 0; | |
4126 | - signalStrength.LTE_SignalStrength.rsrp = 0; | |
4127 | - signalStrength.LTE_SignalStrength.rsrq = 0; | |
4128 | - signalStrength.LTE_SignalStrength.rssnr = 0; | |
4129 | - signalStrength.LTE_SignalStrength.cqi = 0; | |
4130 | 5017 | |
4131 | 5018 | ALOGI("Signal Strength %d", rssi); |
4132 | 5019 | |
4133 | - RIL_onUnsolicitedResponse(RIL_UNSOL_SIGNAL_STRENGTH, &signalStrength, sizeof(signalStrength)); | |
5020 | + RIL_onUnsolicitedResponse(RIL_UNSOL_SIGNAL_STRENGTH, &signalStrength, sizeof(RIL_SignalStrength_v6)); | |
4134 | 5021 | free(line); |
4135 | 5022 | return; |
4136 | 5023 |
@@ -4141,18 +5028,181 @@ error: | ||
4141 | 5028 | return; |
4142 | 5029 | } |
4143 | 5030 | |
5031 | +/* Get SIM status: 1=valid, all the others are invalid */ | |
5032 | +static int getSimState(void) | |
5033 | +{ | |
5034 | + ATResponse *atResponse = NULL; | |
5035 | + int err; | |
5036 | + char *line; | |
5037 | + int status; | |
5038 | + int srv_status,srv_domain,roam_status,sys_mode,sim_state = -1; | |
5039 | + | |
5040 | + // Check status. | |
5041 | + err = at_send_command_singleline("AT^SYSINFO", "^SYSINFO:", &atResponse); | |
5042 | + if (err != AT_NOERROR) | |
5043 | + goto error; | |
5044 | + | |
5045 | + line = atResponse->p_intermediates->line; | |
5046 | + err = at_tok_start(&line); | |
5047 | + if (err < 0) goto error; | |
5048 | + | |
5049 | + err = at_tok_nextint(&line, &srv_status); | |
5050 | + if (err < 0) goto error; | |
5051 | + | |
5052 | + err = at_tok_nextint(&line, &srv_domain); | |
5053 | + if (err < 0) goto error; | |
5054 | + | |
5055 | + err = at_tok_nextint(&line, &roam_status); | |
5056 | + if (err < 0) goto error; | |
5057 | + | |
5058 | + err = at_tok_nextint(&line, &sys_mode); | |
5059 | + if (err < 0) goto error; | |
5060 | + | |
5061 | + err = at_tok_nextint(&line, &sim_state); | |
5062 | + if (err < 0) goto error; | |
5063 | + | |
5064 | + | |
5065 | + ALOGE("Sim State: %d",sim_state); | |
5066 | + at_response_free(atResponse); | |
5067 | + return sim_state; | |
5068 | + | |
5069 | +error: | |
5070 | + ALOGE("Failed to get sim state"); | |
5071 | + | |
5072 | + at_response_free(atResponse); | |
5073 | + return -1; | |
5074 | +} | |
5075 | + | |
5076 | +static int requestNetworkRegistration() | |
5077 | +{ | |
5078 | + ATResponse *atResponse = NULL; | |
5079 | + int err; | |
5080 | + char *line; | |
5081 | + int srv_status,srv_domain,nr = 0; | |
5082 | + | |
5083 | + // Check status. | |
5084 | + err = at_send_command_singleline("AT^SYSINFO", "^SYSINFO:", &atResponse); | |
5085 | + if (err != AT_NOERROR) | |
5086 | + goto error; | |
5087 | + | |
5088 | + line = atResponse->p_intermediates->line; | |
5089 | + err = at_tok_start(&line); | |
5090 | + if (err < 0) goto error; | |
5091 | + | |
5092 | + err = at_tok_nextint(&line, &srv_status); | |
5093 | + if (err < 0) goto error; | |
5094 | + | |
5095 | + err = at_tok_nextint(&line, &srv_domain); | |
5096 | + if (err < 0) goto error; | |
5097 | + | |
5098 | + nr = (srv_status == 2 && (srv_domain == 2 || srv_domain == 3)); | |
5099 | + | |
5100 | + ALOGE("Network registration (PS) : %s", nr ? "Yes" : "No"); | |
5101 | + at_response_free(atResponse); | |
5102 | + return nr; | |
5103 | + | |
5104 | +error: | |
5105 | + ALOGE("Failed to get network registration"); | |
5106 | + | |
5107 | + at_response_free(atResponse); | |
5108 | + return -1; | |
5109 | +} | |
5110 | + | |
5111 | +/* SIM status change */ | |
5112 | +static void unsolicitedSimStatus(const char * s) | |
5113 | +{ | |
5114 | + int err; | |
5115 | + int state; | |
5116 | + char * line = NULL; | |
5117 | + | |
5118 | + /* | |
5119 | + ^SIMST:0 Invalid USIM card state | |
5120 | + ^SIMST:1 Valid USIM card state | |
5121 | + ^SIMST:2 Invalid USIM card in case of CS | |
5122 | + ^SIMST:3 Invalid USIM card in case of PS | |
5123 | + ^SIMST:4 Invalid USIM card in case of CS or PS | |
5124 | + ^SIMST:255 Card not present | |
5125 | + */ | |
5126 | + | |
5127 | + line = strdup(s); | |
5128 | + | |
5129 | + err = at_tok_start(&line); | |
5130 | + if (err < 0) goto error; | |
5131 | + | |
5132 | + err = at_tok_nextint(&line, &state); | |
5133 | + if (err < 0) goto error; | |
5134 | + | |
5135 | + ALOGD("SIM state: %d",state); | |
5136 | + | |
5137 | + free(line); | |
5138 | + return; | |
5139 | + | |
5140 | +error: | |
5141 | + ALOGI("Error getting state"); | |
5142 | + free(line); | |
5143 | + return; | |
5144 | +} | |
5145 | + | |
5146 | +static void unsolicitedSrvStatus(const char * s) | |
5147 | +{ | |
5148 | + int err; | |
5149 | + int mode; | |
5150 | + char * line = NULL; | |
5151 | + | |
5152 | + /* | |
5153 | + ^SRVST:0 No service | |
5154 | + ^SRVST:1 Restricted service | |
5155 | + ^SRVST:2 Valid service | |
5156 | + ^SRVST:3 Restricted Regional Service | |
5157 | + ^SRVST:4 Power saving and deep sleep | |
5158 | + */ | |
5159 | + | |
5160 | + line = strdup(s); | |
5161 | + | |
5162 | + err = at_tok_start(&line); | |
5163 | + if (err < 0) goto error; | |
5164 | + | |
5165 | + err = at_tok_nextint(&line, &mode); | |
5166 | + if (err < 0) goto error; | |
5167 | + | |
5168 | + free(line); | |
5169 | + return; | |
5170 | + | |
5171 | +error: | |
5172 | + ALOGI("Error getting mode"); | |
5173 | + free(line); | |
5174 | + return; | |
5175 | +} | |
5176 | + | |
5177 | + | |
4144 | 5178 | static void unsolicitedMode(const char * s) |
4145 | 5179 | { |
4146 | 5180 | int err; |
4147 | - int mode1; | |
4148 | - int mode2; | |
5181 | + int android_mode; | |
5182 | + int sys_mode; | |
5183 | + int sys_submode; | |
4149 | 5184 | char * line = NULL; |
4150 | 5185 | |
4151 | 5186 | /* |
4152 | - ^MODE:3,2 indicates GPRS | |
4153 | - ^MODE:3,3 indicates EDGE | |
4154 | - ^MODE:5,4 indicates 3G | |
4155 | - ^MODE:5,5 indicates HSDPA | |
5187 | + ^MODE:<sys_mode>,<sys_submode> | |
5188 | + | |
5189 | + <sys_mode>: System mode. The values are as follows: | |
5190 | + 0 No service. | |
5191 | + 1 AMPS mode (not in use currently) | |
5192 | + 2 CDMA mode (not in use currently) | |
5193 | + 3 GSM/GPRS mode | |
5194 | + 4 HDR mode | |
5195 | + 5 WCDMA mode | |
5196 | + 6 GPS mode | |
5197 | + <sys_submode>: System sub mode. The values are as follows: | |
5198 | + 0 No service. | |
5199 | + 1 GSM mode | |
5200 | + 2 GPRS mode | |
5201 | + 3 EDEG mode | |
5202 | + 4 WCDMA mode | |
5203 | + 5 HSDPA mode | |
5204 | + 6 HSUPA mode | |
5205 | + 7 HSDPA mode and HSUPA mode | |
4156 | 5206 | */ |
4157 | 5207 | |
4158 | 5208 | line = strdup(s); |
@@ -4160,13 +5210,43 @@ static void unsolicitedMode(const char * s) | ||
4160 | 5210 | err = at_tok_start(&line); |
4161 | 5211 | if (err < 0) goto error; |
4162 | 5212 | |
4163 | - err = at_tok_nextint(&line, &mode1); | |
5213 | + err = at_tok_nextint(&line, &sys_mode); | |
4164 | 5214 | if (err < 0) goto error; |
4165 | 5215 | |
4166 | - err = at_tok_nextint(&line, &mode2); | |
5216 | + err = at_tok_nextint(&line, &sys_submode); | |
4167 | 5217 | if (err < 0) goto error; |
4168 | 5218 | |
5219 | + switch (sys_submode) | |
5220 | + { | |
5221 | + case 0: | |
5222 | + android_mode = RADIO_TECH_UNKNOWN; | |
5223 | + break; | |
5224 | + case 1: | |
5225 | + android_mode = RADIO_TECH_GSM; | |
5226 | + break; | |
5227 | + case 2: | |
5228 | + android_mode = RADIO_TECH_GPRS; | |
5229 | + break; | |
5230 | + case 3: | |
5231 | + android_mode = RADIO_TECH_EDGE; | |
5232 | + break; | |
5233 | + case 4: | |
5234 | + android_mode = RADIO_TECH_UMTS; | |
5235 | + break; | |
5236 | + case 5: | |
5237 | + android_mode = RADIO_TECH_HSDPA; | |
5238 | + break; | |
5239 | + case 6: | |
5240 | + android_mode = RADIO_TECH_HSUPA; | |
5241 | + break; | |
5242 | + case 7: | |
5243 | + android_mode = RADIO_TECH_HSPA; | |
5244 | + break; | |
5245 | + } | |
5246 | + | |
4169 | 5247 | free(line); |
5248 | + | |
5249 | + RIL_onUnsolicitedResponse(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, &android_mode, sizeof(int *)); | |
4170 | 5250 | return; |
4171 | 5251 | |
4172 | 5252 | error: |
@@ -4194,12 +5274,6 @@ static void requestSignalStrength(RIL_Token t) | ||
4194 | 5274 | |
4195 | 5275 | memset(&signalStrength, 0, sizeof(RIL_SignalStrength_v6)); |
4196 | 5276 | |
4197 | - signalStrength.LTE_SignalStrength.signalStrength = 0x7FFFFFFF; | |
4198 | - signalStrength.LTE_SignalStrength.rsrp = 0x7FFFFFFF; | |
4199 | - signalStrength.LTE_SignalStrength.rsrq = 0x7FFFFFFF; | |
4200 | - signalStrength.LTE_SignalStrength.rssnr = 0x7FFFFFFF; | |
4201 | - signalStrength.LTE_SignalStrength.cqi = 0x7FFFFFFF; | |
4202 | - | |
4203 | 5277 | err = at_send_command_singleline("AT+CSQ", "+CSQ:", &atResponse); |
4204 | 5278 | if (err != AT_NOERROR) |
4205 | 5279 | goto error; |
@@ -4209,32 +5283,18 @@ static void requestSignalStrength(RIL_Token t) | ||
4209 | 5283 | err = at_tok_start(&line); |
4210 | 5284 | if (err < 0) goto error; |
4211 | 5285 | |
4212 | - err = at_tok_nextint(&line,&rssi); | |
5286 | + err = at_tok_nextint(&line, &rssi); | |
4213 | 5287 | if (err < 0) goto error; |
4214 | 5288 | |
4215 | - signalStrength.GW_SignalStrength.signalStrength = rssi; | |
4216 | - | |
4217 | 5289 | err = at_tok_nextint(&line, &ber); |
4218 | - if (err < 0) | |
4219 | - goto error; | |
5290 | + if (err < 0) goto error; | |
4220 | 5291 | |
5292 | + signalStrength.GW_SignalStrength.signalStrength = rssi; | |
4221 | 5293 | signalStrength.GW_SignalStrength.bitErrorRate = ber; |
4222 | 5294 | |
4223 | - signalStrength.CDMA_SignalStrength.dbm = 0; | |
4224 | - signalStrength.CDMA_SignalStrength.ecio = 0; | |
4225 | - signalStrength.EVDO_SignalStrength.dbm = 0; | |
4226 | - signalStrength.EVDO_SignalStrength.ecio = 0; | |
4227 | - signalStrength.EVDO_SignalStrength.signalNoiseRatio = 0; | |
4228 | - signalStrength.LTE_SignalStrength.signalStrength = 0; | |
4229 | - signalStrength.LTE_SignalStrength.rsrp = 0; | |
4230 | - signalStrength.LTE_SignalStrength.rsrq = 0; | |
4231 | - signalStrength.LTE_SignalStrength.rssnr = 0; | |
4232 | - signalStrength.LTE_SignalStrength.cqi = 0; | |
4233 | - | |
4234 | 5295 | ALOGI("SignalStrength %d BER: %d", rssi, ber); |
4235 | 5296 | |
4236 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, &signalStrength, | |
4237 | - sizeof(RIL_SignalStrength_v6)); | |
5297 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, &signalStrength, sizeof(RIL_SignalStrength_v6)); | |
4238 | 5298 | |
4239 | 5299 | at_response_free(atResponse); |
4240 | 5300 | atResponse = NULL; |
@@ -4266,9 +5326,9 @@ static void requestOperator(RIL_Token t) | ||
4266 | 5326 | |
4267 | 5327 | memset(response, 0, sizeof(response)); |
4268 | 5328 | |
4269 | - err = at_send_command_multiline | |
4270 | - ("AT+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", "+COPS:", | |
4271 | - &atResponse); | |
5329 | + err = at_send_command_multiline( | |
5330 | + "AT+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", "+COPS:", | |
5331 | + &atResponse); | |
4272 | 5332 | |
4273 | 5333 | if (err != AT_NOERROR) |
4274 | 5334 | goto error; |
@@ -4279,8 +5339,8 @@ static void requestOperator(RIL_Token t) | ||
4279 | 5339 | * +COPS: 0,2,"310170" |
4280 | 5340 | */ |
4281 | 5341 | for (i = 0, cursor = atResponse->p_intermediates; |
4282 | - cursor != NULL && i < num_resp_lines; | |
4283 | - cursor = cursor->p_next, i++) { | |
5342 | + cursor != NULL && i < num_resp_lines; | |
5343 | + cursor = cursor->p_next, i++) { | |
4284 | 5344 | char *line = cursor->line; |
4285 | 5345 | |
4286 | 5346 | err = at_tok_start(&line); |
@@ -4367,8 +5427,7 @@ static void requestRadioPower(void *data, size_t datalen, RIL_Token t) | ||
4367 | 5427 | if (onOff == 0 && getRadioState() != RADIO_STATE_OFF) { |
4368 | 5428 | char value[PROPERTY_VALUE_MAX]; |
4369 | 5429 | |
4370 | - /* Switch RF OFF */ | |
4371 | - err = at_send_command("AT^RFSWITCH=0"); | |
5430 | + err = at_send_command("AT+CFUN=0"); | |
4372 | 5431 | if (err != AT_NOERROR) |
4373 | 5432 | goto error; |
4374 | 5433 |
@@ -4379,12 +5438,9 @@ static void requestRadioPower(void *data, size_t datalen, RIL_Token t) | ||
4379 | 5438 | |
4380 | 5439 | } else if (onOff > 0 && getRadioState() == RADIO_STATE_OFF) { |
4381 | 5440 | |
4382 | - /* Switch RF ON */ | |
4383 | - err = at_send_command("AT^RFSWITCH=1"); | |
4384 | - if (err != AT_NOERROR) | |
4385 | - goto error; | |
4386 | - | |
5441 | + err = at_send_command("AT+CFUN=1"); | |
4387 | 5442 | if (err != AT_NOERROR) { |
5443 | + | |
4388 | 5444 | // Some stacks return an error when there is no SIM, |
4389 | 5445 | // but they really turn the RF portion on |
4390 | 5446 | // So, if we get an error, let's check to see if it |
@@ -4401,7 +5457,7 @@ static void requestRadioPower(void *data, size_t datalen, RIL_Token t) | ||
4401 | 5457 | |
4402 | 5458 | restricted = RIL_RESTRICTED_STATE_NONE; |
4403 | 5459 | RIL_onUnsolicitedResponse(RIL_UNSOL_RESTRICTED_STATE_CHANGED, |
4404 | - &restricted, sizeof(int *)); | |
5460 | + &restricted, sizeof(int *)); | |
4405 | 5461 | |
4406 | 5462 | return; |
4407 | 5463 |
@@ -4430,7 +5486,31 @@ static void requestGetIMSI(RIL_Token t) | ||
4430 | 5486 | at_response_free(atResponse); |
4431 | 5487 | } |
4432 | 5488 | |
5489 | +/** | |
5490 | + * RIL_REQUEST_GET_IMEI | |
5491 | + * | |
5492 | + * Get the device IMEI, which should be 15 decimal digits. | |
5493 | + */ | |
5494 | +static void requestGetIMEI(RIL_Token t) | |
5495 | +{ | |
5496 | + ATResponse *atResponse = NULL; | |
5497 | + int err; | |
4433 | 5498 | |
5499 | + /* IMEI */ | |
5500 | + err = at_send_command_numeric("AT+CGSN", &atResponse); | |
5501 | + | |
5502 | + if (err != AT_NOERROR) | |
5503 | + goto error; | |
5504 | + | |
5505 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, atResponse->p_intermediates->line, sizeof(char *)); | |
5506 | + | |
5507 | + at_response_free(atResponse); | |
5508 | + return; | |
5509 | + | |
5510 | +error: | |
5511 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
5512 | + at_response_free(atResponse); | |
5513 | +} | |
4434 | 5514 | |
4435 | 5515 | /** |
4436 | 5516 | * RIL_REQUEST_GET_IMEISV |
@@ -4442,9 +5522,8 @@ static void requestGetIMEISV(RIL_Token t) | ||
4442 | 5522 | RIL_onRequestComplete(t, RIL_E_SUCCESS, (void*)"01", sizeof(char *)); |
4443 | 5523 | } |
4444 | 5524 | |
4445 | - | |
4446 | - | |
4447 | -/* RIL_REQUEST_DEVICE_IDENTITY | |
5525 | +/** | |
5526 | + * RIL_REQUEST_DEVICE_IDENTITY | |
4448 | 5527 | * |
4449 | 5528 | * Request the device ESN / MEID / IMEI / IMEISV. |
4450 | 5529 | * |
@@ -4509,6 +5588,31 @@ static void requestBasebandVersion(RIL_Token t) | ||
4509 | 5588 | at_response_free(atResponse); |
4510 | 5589 | } |
4511 | 5590 | |
5591 | +/** | |
5592 | + * RIL_REQUEST_STK_SETUP_CALL | |
5593 | + * | |
5594 | + */ | |
5595 | +static void requestStkSetupCall(void * data, size_t datalen, RIL_Token t) | |
5596 | +{ | |
5597 | + int onOff; | |
5598 | + int err; | |
5599 | + | |
5600 | + if (datalen < sizeof(int *)) { | |
5601 | + ALOGE("%s() bad data length!", __func__); | |
5602 | + goto error; | |
5603 | + } | |
5604 | + | |
5605 | + onOff = ((int *)data)[0]; | |
5606 | + err = at_send_command("AT^CSTC=%d", (onOff)?1:0 ); | |
5607 | + if (err != AT_NOERROR) | |
5608 | + goto error; | |
5609 | + | |
5610 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); | |
5611 | + return; | |
5612 | + | |
5613 | +error: | |
5614 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
5615 | +} | |
4512 | 5616 | |
4513 | 5617 | /** |
4514 | 5618 | * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE |
@@ -4518,9 +5622,21 @@ static void requestBasebandVersion(RIL_Token t) | ||
4518 | 5622 | */ |
4519 | 5623 | static void requestStkSendTerminalResponse(void * data, size_t datalen, RIL_Token t) |
4520 | 5624 | { |
5625 | + int err; | |
5626 | + const char *ec = (const char *) data; | |
4521 | 5627 | (void)datalen; |
4522 | - const char *stkResponse = (const char *) data; | |
4523 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); | |
5628 | + | |
5629 | + err = at_send_command("AT^CSTR=%d,\"%s\"",strlen(ec),ec); | |
5630 | + | |
5631 | + if (err != AT_NOERROR) | |
5632 | + goto error; | |
5633 | + | |
5634 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL,0 ); | |
5635 | + return; | |
5636 | + | |
5637 | +error: | |
5638 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
5639 | + | |
4524 | 5640 | } |
4525 | 5641 | |
4526 | 5642 |
@@ -4532,9 +5648,20 @@ static void requestStkSendTerminalResponse(void * data, size_t datalen, RIL_Toke | ||
4532 | 5648 | */ |
4533 | 5649 | static void requestStkSendEnvelopeCommand(void * data, size_t datalen, RIL_Token t) |
4534 | 5650 | { |
5651 | + int err; | |
4535 | 5652 | const char *ec = (const char *) data; |
4536 | 5653 | (void)datalen; |
4537 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, (void*)"ok", sizeof(char *)); | |
5654 | + | |
5655 | + err = at_send_command("AT^CSEN=%d,\"%s\"",strlen(ec),ec); | |
5656 | + | |
5657 | + if (err != AT_NOERROR) | |
5658 | + goto error; | |
5659 | + | |
5660 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0 ); | |
5661 | + return; | |
5662 | + | |
5663 | +error: | |
5664 | + RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
4538 | 5665 | } |
4539 | 5666 | |
4540 | 5667 |
@@ -4551,6 +5678,11 @@ static void requestStkGetProfile(RIL_Token t) | ||
4551 | 5678 | } |
4552 | 5679 | |
4553 | 5680 | |
5681 | +static void initStackUnsolv() | |
5682 | +{ | |
5683 | + at_send_command("AT^CSMN"); | |
5684 | +} | |
5685 | + | |
4554 | 5686 | /** |
4555 | 5687 | * RIL_REQUEST_STK_SET_PROFILE |
4556 | 5688 | * |
@@ -4581,7 +5713,9 @@ void requestReportStkServiceIsRunning(RIL_Token t) | ||
4581 | 5713 | RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
4582 | 5714 | } |
4583 | 5715 | |
4584 | - | |
5716 | +/** | |
5717 | + * RIL_REQUEST_DIAL | |
5718 | + */ | |
4585 | 5719 | static void requestDial(void *data, size_t datalen, RIL_Token t) |
4586 | 5720 | { |
4587 | 5721 | RIL_Dial *p_dial; |
@@ -4600,6 +5734,9 @@ static void requestDial(void *data, size_t datalen, RIL_Token t) | ||
4600 | 5734 | /* Originate call */ |
4601 | 5735 | ret = at_send_command("ATD%s%s;", p_dial->address, clir); |
4602 | 5736 | |
5737 | + /* Switch to audio from USB1 */ | |
5738 | + at_send_command("AT^DDSETEX=2"); | |
5739 | + | |
4603 | 5740 | /* success or failure is ignored by the upper layer here. |
4604 | 5741 | it will call GET_CURRENT_CALLS and determine success that way */ |
4605 | 5742 | RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
@@ -4614,6 +5751,11 @@ static void requestHangup(void *data, size_t datalen, RIL_Token t) | ||
4614 | 5751 | |
4615 | 5752 | p_line = (int *)data; |
4616 | 5753 | |
5754 | + /* Just in case, stop the audio tunnel now. We need to stop if before the modem | |
5755 | + stops streaming audio. If another call is being held, it will be restarted a | |
5756 | + little bit later by the ccurrent call list callback */ | |
5757 | + stopAudioTunnel(NULL); | |
5758 | + | |
4617 | 5759 | // 3GPP 22.030 6.5.5 |
4618 | 5760 | // "Releases a specific active call X" |
4619 | 5761 | ret = at_send_command("AT+CHLD=1%d", p_line[0]); |
@@ -4625,6 +5767,11 @@ static void requestHangup(void *data, size_t datalen, RIL_Token t) | ||
4625 | 5767 | |
4626 | 5768 | static void requestHangupWaitingOrBackground(RIL_Token t) |
4627 | 5769 | { |
5770 | + /* Just in case, stop the audio tunnel now. We need to stop if before the modem | |
5771 | + stops streaming audio. If another call is being hold, it will be restarted a | |
5772 | + little bit later by the ccurrent call list callback */ | |
5773 | + stopAudioTunnel(NULL); | |
5774 | + | |
4628 | 5775 | // 3GPP 22.030 6.5.5 |
4629 | 5776 | // "Releases all held calls or sets User Determined User Busy |
4630 | 5777 | // (UDUB) for a waiting call." |
@@ -4638,6 +5785,11 @@ static void requestHangupWaitingOrBackground(RIL_Token t) | ||
4638 | 5785 | |
4639 | 5786 | static void requestHangupForegroundResumeBackground(RIL_Token t) |
4640 | 5787 | { |
5788 | + /* Just in case, stop the audio tunnel now. We need to stop if before the modem | |
5789 | + stops streaming audio. If another call is being hold, it will be restarted a | |
5790 | + little bit later */ | |
5791 | + stopAudioTunnel(NULL); | |
5792 | + | |
4641 | 5793 | // 3GPP 22.030 6.5.5 |
4642 | 5794 | // "Releases all active calls (if any exist) and accepts |
4643 | 5795 | // the other (held or waiting) call." |
@@ -4651,6 +5803,11 @@ static void requestHangupForegroundResumeBackground(RIL_Token t) | ||
4651 | 5803 | |
4652 | 5804 | static void requestSwitchWaitingOrHoldingAndActive(RIL_Token t) |
4653 | 5805 | { |
5806 | + /* Just in case, stop the audio tunnel now. We need to stop if before the modem | |
5807 | + stops streaming audio. If another call is being hold, it will be restarted a | |
5808 | + little bit later */ | |
5809 | + stopAudioTunnel(NULL); | |
5810 | + | |
4654 | 5811 | // 3GPP 22.030 6.5.5 |
4655 | 5812 | // "Places all active calls (if any exist) on hold and accepts |
4656 | 5813 | // the other (held or waiting) call." |
@@ -4664,8 +5821,12 @@ static void requestSwitchWaitingOrHoldingAndActive(RIL_Token t) | ||
4664 | 5821 | |
4665 | 5822 | static void requestAnswer(RIL_Token t) |
4666 | 5823 | { |
5824 | + /* Answer call */ | |
4667 | 5825 | at_send_command("ATA"); |
4668 | 5826 | |
5827 | + /* Switch to audio from USB1 */ | |
5828 | + at_send_command("AT^DDSETEX=2"); | |
5829 | + | |
4669 | 5830 | /* success or failure is ignored by the upper layer here. |
4670 | 5831 | it will call GET_CURRENT_CALLS and determine success that way */ |
4671 | 5832 | RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
@@ -4712,24 +5873,24 @@ static void requestSeparateConnection(void * data, size_t datalen, RIL_Token t) | ||
4712 | 5873 | } |
4713 | 5874 | } |
4714 | 5875 | |
4715 | - | |
5876 | +static int wasMuted = 0; | |
4716 | 5877 | static void requestSetMute(void *data, size_t datalen, RIL_Token t) |
4717 | 5878 | { |
4718 | 5879 | int err; |
4719 | 5880 | assert (datalen >= sizeof(int *)); |
5881 | + int mute = ((int*)data)[0]; | |
4720 | 5882 | |
4721 | - /* mute */ | |
4722 | - err = at_send_command("AT+CMUT=%d", ((int*)data)[0]); | |
4723 | - if (err != AT_NOERROR) | |
4724 | - goto error; | |
5883 | + /* Store mute status */ | |
5884 | + wasMuted = mute; | |
4725 | 5885 | |
4726 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); | |
4727 | - return; | |
5886 | + /* mute - Don't consider it an error if it fails, as we are using a Software loopback for audio data */ | |
5887 | + err = at_send_command("AT+CMUT=%d", mute); | |
4728 | 5888 | |
4729 | -error: | |
4730 | - ALOGE("ERROR: requestSetMute failed"); | |
4731 | - RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
5889 | + /* Update the audio tunnel mute status */ | |
5890 | + muteAudioTunnel(wasMuted); | |
4732 | 5891 | |
5892 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); | |
5893 | + return; | |
4733 | 5894 | } |
4734 | 5895 | |
4735 | 5896 |
@@ -4740,8 +5901,8 @@ static void requestGetMute(RIL_Token t) | ||
4740 | 5901 | int response[1]; |
4741 | 5902 | char *line; |
4742 | 5903 | |
5904 | + /* If we fail, return a cached version of it */ | |
4743 | 5905 | err = at_send_command_singleline("AT+CMUT?", "+CMUT:", &atResponse); |
4744 | - | |
4745 | 5906 | if (err != AT_NOERROR) |
4746 | 5907 | goto error; |
4747 | 5908 |
@@ -4759,8 +5920,10 @@ static void requestGetMute(RIL_Token t) | ||
4759 | 5920 | return; |
4760 | 5921 | |
4761 | 5922 | error: |
4762 | - ALOGE("ERROR: requestGetMute failed"); | |
4763 | - RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
5923 | + ALOGE("ERROR: requestGetMute failed - Returned cached result"); | |
5924 | + | |
5925 | + response[0] = wasMuted; | |
5926 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(char*)); | |
4764 | 5927 | at_response_free(atResponse); |
4765 | 5928 | } |
4766 | 5929 |
@@ -4770,7 +5933,14 @@ static void requestDTMF(void * data, size_t datalen, RIL_Token t) | ||
4770 | 5933 | int err = 0; |
4771 | 5934 | char c = ((char *)data)[0]; |
4772 | 5935 | |
4773 | - err = at_send_command("AT+VTS=%c", (int)c); | |
5936 | + /* Start DTMF generation for 250ms */ | |
5937 | + err = at_send_command("AT^DTMF=%d,%c", lastCallIdx, (int)c); /* 1=call idx */ | |
5938 | + | |
5939 | + /* If failed, try the older method */ | |
5940 | + if (err != AT_NOERROR) { | |
5941 | + err = at_send_command("AT+VTS=%c", (int)c); | |
5942 | + } | |
5943 | + | |
4774 | 5944 | if (err != AT_NOERROR) { |
4775 | 5945 | RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
4776 | 5946 | } else { |
@@ -4798,7 +5968,6 @@ error: | ||
4798 | 5968 | |
4799 | 5969 | } |
4800 | 5970 | |
4801 | - | |
4802 | 5971 | static void requestDtmfStart(void *data, size_t datalen, RIL_Token t) |
4803 | 5972 | { |
4804 | 5973 | int err; |
@@ -4808,8 +5977,14 @@ static void requestDtmfStart(void *data, size_t datalen, RIL_Token t) | ||
4808 | 5977 | |
4809 | 5978 | c = ((char *)data)[0]; |
4810 | 5979 | |
4811 | - /* Start DTMF generation fro 250ms */ | |
4812 | - err = at_send_command("AT^DTMF=0,%c", (int)c); | |
5980 | + /* Start DTMF generation for 250ms */ | |
5981 | + err = at_send_command("AT^DTMF=%d,%c", lastCallIdx, (int)c); /* 1=call idx */ | |
5982 | + | |
5983 | + /* If failed, try the older method */ | |
5984 | + if (err != AT_NOERROR) { | |
5985 | + err = at_send_command("AT+VTS=%c", (int)c); | |
5986 | + } | |
5987 | + | |
4813 | 5988 | if (err != AT_NOERROR) |
4814 | 5989 | goto error; |
4815 | 5990 |
@@ -4822,46 +5997,99 @@ error: | ||
4822 | 5997 | |
4823 | 5998 | } |
4824 | 5999 | |
6000 | +/* decodes USSDs. Sometimes, they arrive as GSM8, sometimes they arrive as HEX encoded GSM7... */ | |
6001 | +static bytes_t decodeUSSD(const char* message) | |
6002 | +{ | |
6003 | + bytes_t utf8 = NULL; | |
6004 | + | |
6005 | + /* Could be in Hex or in GSM8 ... - Check it - Some modems return HEX | |
6006 | + encoded GSM7, others return GSM8... */ | |
6007 | + int pos = 0; | |
6008 | + int ishex = 1; | |
6009 | + while (1) { | |
6010 | + unsigned char c = message[pos]; | |
6011 | + if (!c) break; | |
6012 | + if (!((c >= '0' && c <= '9') || ( c >= 'A' && c <= 'F' ))) { | |
6013 | + ishex = 0; | |
6014 | + break; | |
6015 | + } | |
6016 | + pos++; | |
6017 | + }; | |
6018 | + | |
6019 | + /* Not hex encoded. Just convert to UTF-8 from GSM8 */ | |
6020 | + if (!ishex) { | |
6021 | + | |
6022 | + /* Convert GSM8 to UTF8 */ | |
6023 | + int gsmbytes = strlen(message); | |
6024 | + int utf8bytes = utf8_from_gsm8((cbytes_t)message,gsmbytes,NULL); | |
6025 | + utf8 = malloc( utf8bytes + 1); | |
6026 | + utf8_from_gsm8((cbytes_t)message,gsmbytes,utf8); | |
6027 | + utf8[utf8bytes]= '\0'; | |
6028 | + | |
6029 | + } else { | |
6030 | + | |
6031 | + int gsmseptets; | |
6032 | + int utf8bytes; | |
6033 | + | |
6034 | + /* Convert HEX to GSM7 */ | |
6035 | + int hexlen = strlen(message); | |
6036 | + int binlen = (hexlen + 1) >> 1; | |
6037 | + bytes_t bytes = malloc(binlen + 1); | |
6038 | + gsm_hex_to_bytes((cbytes_t)message,hexlen,bytes); | |
6039 | + bytes[binlen] = 0; | |
6040 | + | |
6041 | + /* And then, GSM7 to UTF8 */ | |
6042 | + gsmseptets = (binlen * 8) / 7; // Round DOWN | |
6043 | + utf8bytes = utf8_from_gsm7(bytes, 0, gsmseptets, NULL); | |
6044 | + utf8 = malloc(utf8bytes + 1); | |
6045 | + utf8_from_gsm7(bytes, 0, gsmseptets, utf8); | |
6046 | + utf8[utf8bytes] = 0; | |
6047 | + free(bytes); | |
6048 | + } | |
6049 | + | |
6050 | + return utf8; | |
6051 | +} | |
6052 | + | |
4825 | 6053 | static void unsolicitedUSSD(const char *s) |
4826 | 6054 | { |
4827 | 6055 | char *line, *linestart; |
4828 | - int typeCode, count, err, len; | |
6056 | + int typeCode, count, err; | |
4829 | 6057 | char *message; |
4830 | - char *outputmessage; | |
4831 | 6058 | char *responseStr[2] = {0,0}; |
4832 | 6059 | |
4833 | 6060 | D("unsolicitedUSSD %s", s); |
4834 | 6061 | |
4835 | - linestart=line=strdup(s); | |
6062 | + linestart = line = strdup(s); | |
6063 | + | |
4836 | 6064 | err = at_tok_start(&line); |
4837 | 6065 | if (err < 0) goto error; |
4838 | 6066 | |
4839 | 6067 | err = at_tok_nextint(&line, &typeCode); |
4840 | 6068 | if (err < 0) goto error; |
4841 | 6069 | |
6070 | + if (typeCode < 0 || typeCode > 5) goto error; | |
6071 | + | |
4842 | 6072 | if (at_tok_hasmore(&line)) { |
6073 | + | |
4843 | 6074 | err = at_tok_nextstr(&line, &message); |
4844 | 6075 | if(err < 0) goto error; |
4845 | 6076 | |
4846 | - outputmessage = (char*) malloc(strlen(message)*2+1); | |
4847 | - gsm_hex_to_bytes((cbytes_t)message,strlen(message),(bytes_t)outputmessage); | |
4848 | - | |
4849 | - responseStr[1] = (char*) malloc(strlen(outputmessage)*2+1); | |
4850 | - | |
4851 | - len = utf8_from_gsm8((cbytes_t)outputmessage,strlen(outputmessage),(bytes_t)responseStr[1]); | |
4852 | - | |
4853 | - responseStr[1][strlen(message)/2]='\0'; | |
4854 | - free(outputmessage); | |
6077 | + /* Decode message */ | |
6078 | + responseStr[1] = (char*) decodeUSSD(message); | |
4855 | 6079 | |
4856 | 6080 | count = 2; |
6081 | + | |
4857 | 6082 | } else { |
4858 | - responseStr[1]=NULL; | |
6083 | + | |
6084 | + responseStr[1] = NULL; | |
4859 | 6085 | count = 1; |
4860 | 6086 | } |
4861 | - free(linestart); | |
6087 | + | |
4862 | 6088 | asprintf(&responseStr[0], "%d", typeCode); |
4863 | 6089 | |
4864 | - RIL_onUnsolicitedResponse (RIL_UNSOL_ON_USSD, responseStr, count*sizeof(char*)); | |
6090 | + RIL_onUnsolicitedResponse(RIL_UNSOL_ON_USSD, responseStr, count*sizeof(char*)); | |
6091 | + | |
6092 | + free(linestart); | |
4865 | 6093 | if (responseStr[0]) |
4866 | 6094 | free(responseStr[0]); |
4867 | 6095 | if (responseStr[1]) |
@@ -5103,23 +6331,84 @@ static void requestSetCLIR(void *data, size_t datalen, RIL_Token t) | ||
5103 | 6331 | static void requestSendUSSD(void *data, size_t datalen, RIL_Token t) |
5104 | 6332 | { |
5105 | 6333 | int err = 0; |
5106 | - int len; | |
5107 | - cbytes_t ussdRequest; | |
5108 | - bytes_t temp; | |
5109 | - char *newUSSDRequest; | |
5110 | - ussdRequest = (cbytes_t)(data); | |
5111 | - | |
5112 | - temp = (bytes_t) malloc(strlen((char *)ussdRequest)*sizeof(char)+1); | |
5113 | - len = utf8_to_gsm8(ussdRequest,strlen((char *)ussdRequest),temp); | |
5114 | - newUSSDRequest = (char*) malloc(2*len*sizeof(char)+1); | |
5115 | - gsm_hex_from_bytes(newUSSDRequest,temp, len); | |
5116 | - newUSSDRequest[2*len]='\0'; | |
5117 | - free(temp); | |
5118 | - | |
5119 | - err = at_send_command("AT+CUSD=1,\"%s\",15", newUSSDRequest); | |
5120 | - free(newUSSDRequest); | |
5121 | - if (err != AT_NOERROR) | |
5122 | - goto error; | |
6334 | + cbytes_t ussdRequest = (cbytes_t)(data); | |
6335 | + | |
6336 | + /* | |
6337 | + * ussdRequest should be checked for invalid characters that can be used to | |
6338 | + * send invalid AT commands. However Android have complete check of | |
6339 | + * ussd strings before they are sent to the RIL. | |
6340 | + * | |
6341 | + * AT+CUSD=[<n>[,<str>[,<dcs>]]] | |
6342 | + * <n> = 0,1,2 Dissable, Enable, Cancel | |
6343 | + * <str> = CUSD string in UTF8 | |
6344 | + * <dcs> = Cell Brodcast Data Coding Scheme: | |
6345 | + * 0000 German | |
6346 | + * 0001 English | |
6347 | + * 0010 Italian | |
6348 | + * 0011 French | |
6349 | + * 0100 Spanish | |
6350 | + * 0101 Dutch | |
6351 | + * 0110 Swedish | |
6352 | + * 0111 Danish | |
6353 | + * 1000 Portuguese | |
6354 | + * 1001 Finnish | |
6355 | + * 1010 Norwegian | |
6356 | + * 1011 Greek | |
6357 | + * 1100 Turkish | |
6358 | + * 1101..1110 Reserved for European languages | |
6359 | + * 1111 Language unspecified | |
6360 | + * | |
6361 | + * According to Android ril.h , CUSD messages are always sent as utf8, | |
6362 | + * but the dcs field does not have an entry for this. | |
6363 | + * The nearest "most correct" would be 15 = unspecified, | |
6364 | + * not adding the dcs would result in the default "0" meaning German, | |
6365 | + * and some networks are not happy with this. | |
6366 | + * | |
6367 | + * Huawei modems, depending on firmware version, seem to accept GSM8 or | |
6368 | + * a HEX encoded GSM7 request. Try GSM8; if it fails, try GSM7 encoded in HEX | |
6369 | + */ | |
6370 | + | |
6371 | + /* Convert to GSM8 from UTF8 */ | |
6372 | + int utf8len = strlen((const char*)ussdRequest); | |
6373 | + int gsmbytes = utf8_to_gsm8(ussdRequest, utf8len, NULL); | |
6374 | + bytes_t gsm8 = malloc(gsmbytes + 1); | |
6375 | + utf8_to_gsm8(ussdRequest, utf8len, gsm8); | |
6376 | + gsm8[gsmbytes] = 0; | |
6377 | + | |
6378 | + /* Try to send it as GSM8 */ | |
6379 | + err = at_send_command("AT+CUSD=1,%s,15", gsm8); | |
6380 | + | |
6381 | + /* Some modems like the string to be between quotes ... Retry if error */ | |
6382 | + if (err != AT_NOERROR) { | |
6383 | + err = at_send_command("AT+CUSD=1,\"%s\",15", gsm8); | |
6384 | + } | |
6385 | + free(gsm8); | |
6386 | + | |
6387 | + /* If modem did not accept the USSD, retry it but using HEX encoded GSM7 */ | |
6388 | + if (err != AT_NOERROR) { | |
6389 | + char* hex = NULL; | |
6390 | + | |
6391 | + /* Convert to GSM7 */ | |
6392 | + int gsmseptets = utf8_to_gsm7(ussdRequest, utf8len, NULL, 0); | |
6393 | + int gsmbytes = ((gsmseptets * 7) + 7) / 8; // Round UP, to be able to transmit incomplete septets | |
6394 | + bytes_t gsm7 = malloc(gsmbytes + 1); | |
6395 | + utf8_to_gsm7(ussdRequest, utf8len, gsm7, 0); | |
6396 | + gsm7[gsmbytes] = 0; | |
6397 | + | |
6398 | + /* And then encode to HEX */ | |
6399 | + hex = malloc(2*gsmbytes+1); | |
6400 | + gsm_hex_from_bytes(hex, gsm7, gsmbytes); | |
6401 | + hex[2*gsmbytes]='\0'; | |
6402 | + | |
6403 | + /* Retry to send the command */ | |
6404 | + ALOGD("utf8len: %d, gsmseptets: %d, gsmbytes:%d", utf8len, gsmseptets, gsmbytes); | |
6405 | + err = at_send_command("AT+CUSD=1,\"%s\",15", hex); | |
6406 | + free(gsm7); | |
6407 | + free(hex); | |
6408 | + | |
6409 | + if (err != AT_NOERROR) | |
6410 | + goto error; | |
6411 | + } | |
5123 | 6412 | |
5124 | 6413 | RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
5125 | 6414 | return; |
@@ -5191,11 +6480,9 @@ static void requestResetRadio(RIL_Token t) | ||
5191 | 6480 | |
5192 | 6481 | /* Reset MS */ |
5193 | 6482 | err = at_send_command("AT+CFUN=6"); |
5194 | - if (err != AT_NOERROR) | |
5195 | - goto error; | |
5196 | 6483 | |
5197 | 6484 | /* Go online */ |
5198 | - err = at_send_command("AT^RFSWITCH=1"); | |
6485 | + err = at_send_command("AT^RFSWITCH=1"); /* ignore error. If unsupported, the next line will enable RF */ | |
5199 | 6486 | err = at_send_command("AT+CFUN=1"); |
5200 | 6487 | if (err != AT_NOERROR) |
5201 | 6488 | goto error; |
@@ -5237,22 +6524,17 @@ static void requestSetLocationUpdates(void *data, size_t datalen, RIL_Token t) | ||
5237 | 6524 | { |
5238 | 6525 | int err = 0; |
5239 | 6526 | int updates = 0; |
5240 | - ATResponse *atResponse = NULL; | |
5241 | 6527 | |
5242 | 6528 | updates = ((int *)data)[0] == 1? 2 : 1; |
5243 | 6529 | |
5244 | - err = at_send_command_singleline("AT+CREG=%d","+CLIP:",&atResponse, updates); | |
6530 | + err = at_send_command("AT+CREG=%d", updates); | |
5245 | 6531 | if (err != AT_NOERROR) |
5246 | 6532 | goto error; |
5247 | 6533 | |
5248 | - at_response_free(atResponse); | |
5249 | - | |
5250 | - //Always return success for CDMA (for now) | |
5251 | 6534 | RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); |
5252 | 6535 | return; |
5253 | 6536 | |
5254 | 6537 | error: |
5255 | - at_response_free(atResponse); | |
5256 | 6538 | RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); |
5257 | 6539 | } |
5258 | 6540 |
@@ -5268,6 +6550,16 @@ static void requestLastFailCause(RIL_Token t) | ||
5268 | 6550 | if(err != AT_NOERROR) |
5269 | 6551 | goto error; |
5270 | 6552 | |
6553 | + /* | |
6554 | + CALL_FAIL_NORMAL = 16, | |
6555 | + CALL_FAIL_BUSY = 17, | |
6556 | + CALL_FAIL_CONGESTION = 34, | |
6557 | + CALL_FAIL_ACM_LIMIT_EXCEEDED = 68, | |
6558 | + CALL_FAIL_CALL_BARRED = 240, | |
6559 | + CALL_FAIL_FDN_BLOCKED = 241, | |
6560 | + CALL_FAIL_ERROR_UNSPECIFIED = 0xffff | |
6561 | + */ | |
6562 | + | |
5271 | 6563 | line = atResponse->p_intermediates->line; |
5272 | 6564 | err = at_tok_start(&line); |
5273 | 6565 | if(err < 0) goto error; |
@@ -5275,17 +6567,19 @@ static void requestLastFailCause(RIL_Token t) | ||
5275 | 6567 | err = at_tok_nextstr(&line, &tmp); |
5276 | 6568 | if(err < 0) goto error; |
5277 | 6569 | |
5278 | - err = at_tok_nextint(&line, &response); | |
5279 | - if(err < 0) goto error; | |
6570 | + if (at_tok_hasmore(&line)) { | |
6571 | + err = at_tok_nextint(&line, &response); | |
6572 | + if(err < 0) goto error; | |
6573 | + } | |
5280 | 6574 | |
5281 | - RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(int)); | |
5282 | - at_response_free(atResponse); | |
5283 | - return; | |
6575 | + ALOGD("Textual last call fail cause: %s [%d]", tmp, response); | |
5284 | 6576 | |
5285 | 6577 | error: |
5286 | - at_response_free(atResponse); | |
5287 | - RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); | |
5288 | 6578 | |
6579 | + /* Send the last call fail cause */ | |
6580 | + RIL_onRequestComplete(t, RIL_E_SUCCESS, &lastCallFailCause, sizeof(int)); | |
6581 | + at_response_free(atResponse); | |
6582 | + return; | |
5289 | 6583 | } |
5290 | 6584 | |
5291 | 6585 | /* |
@@ -5448,30 +6742,6 @@ static void onSIMReady(void *p) | ||
5448 | 6742 | /* Check if ME is ready to set preferred message storage */ |
5449 | 6743 | checkMessageStorageReady(NULL); |
5450 | 6744 | |
5451 | - /* Select message service */ | |
5452 | - at_send_command("AT+CSMS=0"); | |
5453 | - | |
5454 | - /* | |
5455 | - * Always send SMS messages directly to the TE | |
5456 | - * | |
5457 | - * mode = 1 // discard when link is reserved (link should never be | |
5458 | - * reserved) | |
5459 | - * mt = 2 // most messages routed to TE | |
5460 | - * bm = 2 // new cell BM's routed to TE | |
5461 | - * ds = 1 // Status reports routed to TE | |
5462 | - * bfr = 1 // flush buffer | |
5463 | - */ | |
5464 | - at_send_command("AT+CNMI=1,2,2,1,1"); | |
5465 | - | |
5466 | - /* Select cell broadcast messages: Do not accept messages */ | |
5467 | - at_send_command("AT+CSCB=1"); | |
5468 | - | |
5469 | - /* Enable automatic timezone update */ | |
5470 | - at_send_command("AT+CTZU=1"); | |
5471 | - | |
5472 | - /* Enable timezone change reporting */ | |
5473 | - at_send_command("AT+CTZR=1"); | |
5474 | - | |
5475 | 6745 | /* Subscribe to network registration events. |
5476 | 6746 | * n = 2 - Enable network registration and location information |
5477 | 6747 | * unsolicited result code +CREG: <stat>[,<lac>,<ci>] |
@@ -5482,23 +6752,58 @@ static void onSIMReady(void *p) | ||
5482 | 6752 | at_send_command("AT+CREG=1"); |
5483 | 6753 | } |
5484 | 6754 | |
6755 | + /* Enable proactive network registration notifications */ | |
6756 | + at_send_command("AT+CGREG=2"); | |
6757 | + | |
6758 | + /* Extended Cellular result codes enabled */ | |
6759 | + at_send_command("AT+CRC=1"); | |
6760 | + | |
6761 | + /* Calling line identification enabled */ | |
6762 | + at_send_command("AT+CLIP=1"); | |
6763 | + | |
6764 | + /* Call waiting enabled */ | |
6765 | + at_send_command("AT+CCWA=1"); | |
6766 | + | |
6767 | + /* Alternating voice/data off */ | |
6768 | + at_send_command("AT+CMOD=0"); | |
6769 | + | |
6770 | + /* +CSSU unsolicited supp service notifications */ | |
6771 | + at_send_command("AT+CSSN=0,1"); | |
6772 | + | |
6773 | + /* Connected line identification On*/ | |
6774 | + at_send_command("AT+COLP=1"); | |
6775 | + | |
6776 | + /* | |
6777 | + * Ensure that the modem is using GSM character set and not IRA, | |
6778 | + * otherwise weirdness with umlauts and other non-ASCII characters | |
6779 | + * can result | |
6780 | + */ | |
6781 | + at_send_command("AT+CSCS=\"GSM\""); | |
6782 | + | |
6783 | + /* USSD unsolicited */ | |
6784 | + at_send_command("AT+CUSD=1"); | |
6785 | + | |
5485 | 6786 | /* Configure Short Message (SMS) Format |
5486 | 6787 | * mode = 0 - PDU mode. |
5487 | 6788 | */ |
5488 | 6789 | at_send_command("AT+CMGF=0"); |
5489 | 6790 | |
5490 | - /* Enable unsolicited RSSI reporting */ | |
5491 | - at_send_command("AT^CURC=1"); | |
5492 | - | |
5493 | - /* Enable GPRS reporting */ | |
5494 | - at_send_command("AT+CGEREP=1,0"); | |
6791 | + /* Disable boot messages */ | |
6792 | + at_send_command("AT^BOOT=0,0"); | |
5495 | 6793 | |
6794 | + /* And flow messages */ | |
6795 | + at_send_command("AT^DSFLOWRPT=0"); | |
5496 | 6796 | |
5497 | 6797 | /* Select SMS type */ |
5498 | - at_send_command_singleline("AT+CSMS=1", "+CSMS:", NULL); | |
5499 | -} | |
6798 | + at_send_command_singleline("AT+CSMS=0", "+CSMS:", NULL); | |
6799 | + | |
6800 | + /* Always send SMS messages directly to the TE */ | |
6801 | + at_send_command("AT+CNMI=2,1,2,2,0"); | |
5500 | 6802 | |
6803 | + /* Enable unsolicited RSSI reporting */ | |
6804 | + at_send_command("AT^CURC=1"); | |
5501 | 6805 | |
6806 | +} | |
5502 | 6807 | |
5503 | 6808 | static void requestNotSupported(RIL_Token t, int request) |
5504 | 6809 | { |
@@ -5519,7 +6824,6 @@ static void processRequest (int request, void *data, size_t datalen, RIL_Token t | ||
5519 | 6824 | * GENERIC_FAILURE if we're not in SIM_STATE_READY. |
5520 | 6825 | */ |
5521 | 6826 | RIL_RadioState radio_state = getRadioState(); |
5522 | -D("%s() %s", __func__, radioStateToString(radio_state)); | |
5523 | 6827 | |
5524 | 6828 | if (radio_state != RADIO_STATE_SIM_READY && |
5525 | 6829 | (request == RIL_REQUEST_WRITE_SMS_TO_SIM || |
@@ -5585,11 +6889,11 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5585 | 6889 | /* Trigger a rehash of network values, just to be sure. */ |
5586 | 6890 | if (((int *)data)[0] == 1) |
5587 | 6891 | RIL_onUnsolicitedResponse( |
5588 | - RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, | |
5589 | - NULL, 0); | |
6892 | + RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, | |
6893 | + NULL, 0); | |
5590 | 6894 | break; |
5591 | 6895 | |
5592 | - /* Data Call Requests */ | |
6896 | + /* Data Call Requests */ | |
5593 | 6897 | case RIL_REQUEST_SETUP_DATA_CALL: |
5594 | 6898 | requestSetupDefaultPDP(data, datalen, t); |
5595 | 6899 | break; |
@@ -5603,7 +6907,7 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5603 | 6907 | requestPDPContextList(t); |
5604 | 6908 | break; |
5605 | 6909 | |
5606 | - /* SMS Requests */ | |
6910 | + /* SMS Requests */ | |
5607 | 6911 | case RIL_REQUEST_SEND_SMS: |
5608 | 6912 | requestSendSMS(data, datalen, t); |
5609 | 6913 | break; |
@@ -5638,7 +6942,7 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5638 | 6942 | requestGSMSMSBroadcastActivation(data, datalen, t); |
5639 | 6943 | break; |
5640 | 6944 | |
5641 | - /* SIM Handling Requests */ | |
6945 | + /* SIM Handling Requests */ | |
5642 | 6946 | case RIL_REQUEST_SIM_IO: |
5643 | 6947 | requestSIM_IO(data, datalen, t); |
5644 | 6948 | break; |
@@ -5664,7 +6968,7 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5664 | 6968 | requestSetFacilityLock(data, datalen, t); |
5665 | 6969 | break; |
5666 | 6970 | |
5667 | - /* Network Requests */ | |
6971 | + /* Network Requests */ | |
5668 | 6972 | case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: |
5669 | 6973 | requestEnterSimPin(data, datalen, t, request); |
5670 | 6974 | break; |
@@ -5686,14 +6990,14 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5686 | 6990 | case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: |
5687 | 6991 | requestGetPreferredNetworkType(t); |
5688 | 6992 | break; |
5689 | - case RIL_REQUEST_VOICE_REGISTRATION_STATE: | |
5690 | - requestRegistrationState(t); | |
5691 | - break; | |
5692 | 6993 | case RIL_REQUEST_DATA_REGISTRATION_STATE: |
5693 | - requestGprsRegistrationState(t); | |
6994 | + requestRegistrationState(t, 1); | |
6995 | + break; | |
6996 | + case RIL_REQUEST_VOICE_REGISTRATION_STATE: | |
6997 | + requestRegistrationState(t, 0); | |
5694 | 6998 | break; |
5695 | 6999 | |
5696 | - /* OEM */ | |
7000 | + /* OEM */ | |
5697 | 7001 | case RIL_REQUEST_OEM_HOOK_RAW: |
5698 | 7002 | // echo back data |
5699 | 7003 | requestOEMHookRaw(data, datalen, t); |
@@ -5702,7 +7006,7 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5702 | 7006 | requestOEMHookStrings(data, datalen, t); |
5703 | 7007 | break; |
5704 | 7008 | |
5705 | - /* Misc */ | |
7009 | + /* Misc */ | |
5706 | 7010 | case RIL_REQUEST_SIGNAL_STRENGTH: |
5707 | 7011 | requestSignalStrength(t); |
5708 | 7012 | break; |
@@ -5716,7 +7020,7 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5716 | 7020 | requestGetIMSI(t); |
5717 | 7021 | break; |
5718 | 7022 | case RIL_REQUEST_GET_IMEI: |
5719 | - requestGetIMEISV(t); | |
7023 | + requestGetIMEI(t); | |
5720 | 7024 | break; |
5721 | 7025 | case RIL_REQUEST_GET_IMEISV: |
5722 | 7026 | requestGetIMEISV(t); |
@@ -5728,7 +7032,15 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5728 | 7032 | requestBasebandVersion(t); |
5729 | 7033 | break; |
5730 | 7034 | |
5731 | - /* SIM Application Toolkit */ | |
7035 | + /* Band control */ | |
7036 | + case RIL_REQUEST_SET_BAND_MODE: | |
7037 | + requestSetBandMode(data, datalen, t); | |
7038 | + break; | |
7039 | + case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: | |
7040 | + requestQueryAvailableBandMode(data, datalen, t); | |
7041 | + break; | |
7042 | + | |
7043 | + /* SIM Application Toolkit */ | |
5732 | 7044 | case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: |
5733 | 7045 | requestStkSendTerminalResponse(data, datalen, t); |
5734 | 7046 | break; |
@@ -5745,7 +7057,7 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5745 | 7057 | requestReportStkServiceIsRunning(t); |
5746 | 7058 | break; |
5747 | 7059 | |
5748 | - /* Misc ops */ | |
7060 | + /* Misc ops */ | |
5749 | 7061 | case RIL_REQUEST_DIAL: |
5750 | 7062 | requestDial(data, datalen, t); |
5751 | 7063 | break; |
@@ -5862,7 +7174,7 @@ D("%s() %s", __func__, radioStateToString(radio_state)); | ||
5862 | 7174 | |
5863 | 7175 | default: |
5864 | 7176 | ALOGW("FIXME: Unsupported request logged: %s", |
5865 | - requestToString(request)); | |
7177 | + requestToString(request)); | |
5866 | 7178 | requestNotSupported(t, request); |
5867 | 7179 | break; |
5868 | 7180 | } |
@@ -5892,8 +7204,7 @@ static void onRequest(int request, void *data, size_t datalen, RIL_Token t) | ||
5892 | 7204 | RequestQueue *q = &s_requestQueue; |
5893 | 7205 | int err; |
5894 | 7206 | |
5895 | - r = (RILRequest *) malloc(sizeof(RILRequest)); | |
5896 | - memset(r, 0, sizeof(RILRequest)); | |
7207 | + r = (RILRequest *) calloc(1, sizeof(RILRequest)); | |
5897 | 7208 | |
5898 | 7209 | /* Formulate a RILRequest and put it in the queue. */ |
5899 | 7210 | r->request = request; |
@@ -5986,90 +7297,40 @@ static void sendTime(void *p) | ||
5986 | 7297 | at_send_command("AT+CCLK=\"%s%c%02d\"", str, tz[0], tzi); |
5987 | 7298 | } |
5988 | 7299 | |
5989 | - | |
7300 | +/** | |
7301 | + * Initialize everything | |
7302 | + */ | |
5990 | 7303 | static char initializeCommon(void) |
5991 | 7304 | { |
5992 | 7305 | int err = 0; |
5993 | 7306 | unsigned int i; |
5994 | 7307 | static const char* const initcmd[] = { |
5995 | 7308 | |
5996 | - /* Reset the MS*/ | |
5997 | - "AT+CFUN=6", | |
5998 | - | |
5999 | - /* atchannel is tolerant of echo but it must */ | |
6000 | - /* reset and have verbose result codes */ | |
6001 | - "ATZV1", | |
6002 | - | |
6003 | 7309 | /* echo off */ |
6004 | 7310 | "ATE0", |
6005 | 7311 | |
6006 | - /* No auto-answer */ | |
6007 | - "ATS0=0", | |
7312 | + /* Send verbose results */ | |
7313 | + "ATQ0V1", | |
6008 | 7314 | |
6009 | 7315 | /* No auto-answer */ |
6010 | - "AT%AUTOANSWER=0", | |
6011 | - | |
6012 | - /* send results */ | |
6013 | - "ATQ0", | |
6014 | - | |
6015 | - /* check for busy, don't check for dialone */ | |
6016 | - "ATX3", | |
6017 | - | |
6018 | - /* set DCD depending on service */ | |
6019 | - "AT&C1", | |
6020 | - | |
6021 | - /* set DTR according to service */ | |
6022 | - "AT&D1", | |
7316 | + "ATS0=0", | |
6023 | 7317 | |
6024 | 7318 | /* Extended errors without textual decriptions */ |
6025 | 7319 | "AT+CMEE=1", |
6026 | 7320 | |
6027 | - /* detailed rings, unknown */ | |
6028 | - "AT+CRC=1;+CR=1", | |
6029 | - | |
6030 | - /* caller id = yes */ | |
6031 | - "AT+CLIP=1", | |
6032 | - | |
6033 | - /* don't hide outgoing callerID */ | |
6034 | - "AT+CLIR=0", | |
6035 | - | |
6036 | - /* Call Waiting notifications */ | |
6037 | - "AT+CCWA=1", | |
6038 | - | |
6039 | - /* No connected line identification */ | |
6040 | - "AT+COLP=0", | |
6041 | - | |
6042 | - /* USSD unsolicited */ | |
6043 | - "AT+CUSD=1", | |
6044 | - | |
6045 | - /* SMS PDU mode */ | |
6046 | - "AT+CMGF=0", | |
6047 | - | |
6048 | - //"AT+GTKC=2", | |
6049 | - | |
6050 | - /* +CSSU unsolicited supp service notifications */ | |
6051 | - "AT+CSSN=0,1", | |
6052 | - | |
6053 | - /* HEX character set */ | |
6054 | - //"AT+CSCS=\"HEX\"", | |
6055 | - "AT+CSCS=\"IRA\"", | |
6056 | - | |
6057 | - /* Modem mode */ | |
6058 | - "AT+FCLASS=0", | |
6059 | - | |
6060 | - "AT+CNMI=1,2,2,2,0", | |
6061 | - //"AT+CPPP=1", | |
7321 | + /* Enable RF */ | |
7322 | + "AT^RFSWITCH=1" | |
7323 | + }; | |
6062 | 7324 | |
7325 | + D("%s()", __func__); | |
6063 | 7326 | |
6064 | - "AT" | |
6065 | - }; | |
7327 | + setRadioState(RADIO_STATE_OFF); | |
6066 | 7328 | |
6067 | 7329 | if (at_handshake() < 0) { |
6068 | 7330 | LOG_FATAL("Handshake failed!"); |
6069 | 7331 | return 1; |
6070 | 7332 | } |
6071 | 7333 | |
6072 | - | |
6073 | 7334 | for (i=0; i < sizeof(initcmd) / sizeof(initcmd[0]); i++) { |
6074 | 7335 | err = at_send_command(initcmd[i]); |
6075 | 7336 | if (err != AT_NOERROR) { |
@@ -6077,63 +7338,9 @@ static char initializeCommon(void) | ||
6077 | 7338 | } |
6078 | 7339 | } |
6079 | 7340 | |
6080 | - /* Send the current time of the OS to the module */ | |
6081 | - sendTime(NULL); | |
6082 | - | |
6083 | - return 0; | |
6084 | -} | |
6085 | - | |
6086 | -/** | |
6087 | - * Initialize everything | |
6088 | - */ | |
6089 | -static char initializeChannel(void) | |
6090 | -{ | |
6091 | - int err = 0; | |
6092 | - unsigned int i; | |
6093 | - static const char* const initcmd[] = { | |
6094 | - | |
6095 | - /* Configure Packet Domain Network Registration Status events | |
6096 | - * 2 = Enable network registration and location information | |
6097 | - * unsolicited result code | |
6098 | - */ | |
6099 | - | |
6100 | - /* Alternating voice/data off */ | |
6101 | - "AT+CMOD=0", | |
7341 | + /* TE-MS handshake function */ | |
7342 | + //"AT^HS=0,0", | |
6102 | 7343 | |
6103 | - /* Not muted */ | |
6104 | - "AT+CMUT=0", | |
6105 | - | |
6106 | - /* Network registration events */ | |
6107 | - "AT+CREG=2", | |
6108 | - | |
6109 | - /* GPRS registration events */ | |
6110 | - "AT+CGREG=2", | |
6111 | - | |
6112 | - "AT+CGEQREQ=1,4,0,0,0,0,2,0,\"0E0\",\"0E0\",3,0,0", | |
6113 | - | |
6114 | - /* Enable unsolicited reports */ | |
6115 | - "AT^CURC=1", | |
6116 | - | |
6117 | - /* Enable GPRS reporting */ | |
6118 | - "AT+CGEREP=1,0", | |
6119 | - | |
6120 | - /* for 3G Preferred */ | |
6121 | - "AT^SYSCFG=2,2,3FFFFFFF,1,2", | |
6122 | - | |
6123 | - /* Disable RF */ | |
6124 | - "AT^RFSWITCH=0" | |
6125 | - }; | |
6126 | - | |
6127 | - D("%s()", __func__); | |
6128 | - | |
6129 | - setRadioState(RADIO_STATE_OFF); | |
6130 | - | |
6131 | - for (i=0; i < sizeof(initcmd) / sizeof(initcmd[0]); i++) { | |
6132 | - err = at_send_command(initcmd[i]); | |
6133 | - if (err != AT_NOERROR) { | |
6134 | - ALOGE("Failed sending command '%s'",initcmd[i]); | |
6135 | - } | |
6136 | - } | |
6137 | 7344 | |
6138 | 7345 | /* Assume radio is off on error. */ |
6139 | 7346 | if (isRadioOn() > 0) |
@@ -6154,28 +7361,75 @@ static void onUnsolicited (const char *s, const char *sms_pdu) | ||
6154 | 7361 | if (getRadioState() == RADIO_STATE_UNAVAILABLE) |
6155 | 7362 | return; |
6156 | 7363 | |
7364 | + /* Timezone */ | |
6157 | 7365 | if (strStartsWith(s, "%CTZV:") |
6158 | 7366 | || strStartsWith(s,"+CTZV:") |
6159 | 7367 | || strStartsWith(s,"+CTZDST:") |
6160 | - || strStartsWith(s,"+HTCCTZV:")) { | |
7368 | + || strStartsWith(s,"+HTCCTZV:") | |
7369 | + || strStartsWith(s,"^NWTIME:") | |
7370 | + ) { | |
6161 | 7371 | unsolicitedNitzTime(s); |
7372 | + | |
7373 | + /* Call status/start/end indicator */ | |
6162 | 7374 | } else if (strStartsWith(s,"+CRING:") |
6163 | 7375 | || strStartsWith(s,"RING") |
7376 | + || strStartsWith(s,"+CLIP") | |
6164 | 7377 | || strStartsWith(s,"NO CARRIER") |
6165 | 7378 | || strStartsWith(s,"+CCWA") |
7379 | + || strStartsWith(s,"^ORIG:") | |
7380 | + || strStartsWith(s,"^CONF:") | |
7381 | + || strStartsWith(s,"^CONN:") | |
7382 | + || strStartsWith(s,"^CEND:") | |
6166 | 7383 | ) { |
6167 | - RIL_onUnsolicitedResponse ( | |
7384 | + | |
7385 | + /* Start/stop audio tunnel if needed when a successful connection is done */ | |
7386 | + if ( strStartsWith(s,"^ORIG:")) { | |
7387 | + enqueueRILEvent(startAudioTunnel, NULL, NULL); | |
7388 | + unsolicitedORIG(s); | |
7389 | + } else | |
7390 | + if ( strStartsWith(s,"^CONF:")) { | |
7391 | + enqueueRILEvent(startAudioTunnel, NULL, NULL); | |
7392 | + unsolicitedCONF(s); | |
7393 | + } else | |
7394 | + if ( strStartsWith(s,"^CONN:")) { | |
7395 | + enqueueRILEvent(startAudioTunnel, NULL, NULL); | |
7396 | + unsolicitedCONN(s); | |
7397 | + } else | |
7398 | + if (strStartsWith(s,"^CEND:")) { | |
7399 | + /* This is the only fast way to detect when call was hang by the callee */ | |
7400 | + enqueueRILEvent(stopAudioTunnel, NULL, NULL); | |
7401 | + unsolicitedCEND(s); | |
7402 | + } | |
7403 | + | |
7404 | + RIL_onUnsolicitedResponse( | |
7405 | + RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, | |
7406 | + NULL, 0); | |
7407 | + enqueueRILEvent(onDataCallListChanged, NULL, NULL); | |
7408 | + | |
7409 | + /* Data call status/start/end */ | |
7410 | + } else if (strStartsWith(s,"^DCONN:") | |
7411 | + || strStartsWith(s,"^DEND:")) { | |
7412 | + | |
7413 | + RIL_onUnsolicitedResponse( | |
6168 | 7414 | RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, |
6169 | 7415 | NULL, 0); |
6170 | 7416 | enqueueRILEvent(onDataCallListChanged, NULL, NULL); |
6171 | - } else if (strStartsWith(s,"^RSSI:") || | |
6172 | - strStartsWith(s,"%RSSI:")) { | |
7417 | + | |
7418 | + } else if (strStartsWith(s,"^COMM:")) { | |
7419 | + | |
7420 | + | |
7421 | + } else if (strStartsWith(s,"^RSSI:") | |
7422 | + || strStartsWith(s,"%RSSI:")) { | |
6173 | 7423 | unsolicitedRSSI(s); |
6174 | 7424 | } else if (strStartsWith(s,"^MODE:")) { |
6175 | 7425 | unsolicitedMode(s); |
7426 | + } else if (strStartsWith(s,"^SRVST:")) { | |
7427 | + unsolicitedSrvStatus(s); | |
7428 | + } else if (strStartsWith(s,"^SIMST:")) { | |
7429 | + unsolicitedSimStatus(s); | |
6176 | 7430 | } else if (strStartsWith(s,"+CREG:") |
6177 | 7431 | || strStartsWith(s,"+CGREG:")) { |
6178 | - RIL_onUnsolicitedResponse ( | |
7432 | + RIL_onUnsolicitedResponse( | |
6179 | 7433 | RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, |
6180 | 7434 | NULL, 0); |
6181 | 7435 | enqueueRILEvent(onDataCallListChanged, NULL, NULL); |
@@ -6183,7 +7437,8 @@ static void onUnsolicited (const char *s, const char *sms_pdu) | ||
6183 | 7437 | onNewSms(sms_pdu); |
6184 | 7438 | } else if (strStartsWith(s, "+CBM:")) { |
6185 | 7439 | onNewBroadcastSms(sms_pdu); |
6186 | - } else if (strStartsWith(s, "+CMTI:")) { | |
7440 | + } else if (strStartsWith(s, "+CMTI:") | |
7441 | + || strStartsWith(s, "+CDSI:")) { | |
6187 | 7442 | onNewSmsOnSIM(s); |
6188 | 7443 | } else if (strStartsWith(s, "+CDS:")) { |
6189 | 7444 | onNewStatusReport(sms_pdu); |
@@ -6196,8 +7451,9 @@ static void onUnsolicited (const char *s, const char *sms_pdu) | ||
6196 | 7451 | enqueueRILEvent(onDataCallListChanged, NULL, NULL); |
6197 | 7452 | } else if (strStartsWith(s, "+CUSD:")) { |
6198 | 7453 | unsolicitedUSSD(s); |
6199 | - } else if (strStartsWith(s, "+CIEV: 7") || | |
6200 | - strStartsWith(s, "Received SMS:")) { | |
7454 | + } else if (strStartsWith(s, "+CIEV: 7") | |
7455 | + || strStartsWith(s, "Received SMS:") | |
7456 | + || strStartsWith(s, "^SMMEMFULL") ) { | |
6201 | 7457 | onNewSmsIndication(); |
6202 | 7458 | } |
6203 | 7459 | } |
@@ -6210,16 +7466,16 @@ static void signalCloseQueues(void) | ||
6210 | 7466 | RequestQueue *q = s_requestQueues[i]; |
6211 | 7467 | if ((err = pthread_mutex_lock(&q->queueMutex)) != 0) |
6212 | 7468 | ALOGE("%s() failed to take queue mutex: %s", |
6213 | - __func__, strerror(err)); | |
7469 | + __func__, strerror(err)); | |
6214 | 7470 | |
6215 | 7471 | q->closed = 1; |
6216 | 7472 | if ((err = pthread_cond_signal(&q->cond)) != 0) |
6217 | 7473 | ALOGE("%s() failed to broadcast queue update: %s", |
6218 | - __func__, strerror(err)); | |
7474 | + __func__, strerror(err)); | |
6219 | 7475 | |
6220 | 7476 | if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0) |
6221 | 7477 | ALOGE("%s() failed to take queue mutex: %s", __func__, |
6222 | - strerror(err)); | |
7478 | + strerror(err)); | |
6223 | 7479 | } |
6224 | 7480 | } |
6225 | 7481 |
@@ -6251,7 +7507,7 @@ static void onATTimeout() | ||
6251 | 7507 | static void usage(char *s) |
6252 | 7508 | { |
6253 | 7509 | #ifdef RIL_SHLIB |
6254 | - fprintf(stderr, "htcgeneric-ril requires: -p <tcp port> or -d /dev/tty_device\n"); | |
7510 | + fprintf(stderr, "generic-ril requires: -p <tcp port> or -d /dev/tty_device\n"); | |
6255 | 7511 | #else |
6256 | 7512 | fprintf(stderr, "usage: %s [-p <tcp port>] [-d /dev/tty_device] [-v /dev/tty_device]\n", s); |
6257 | 7513 | exit(-1); |
@@ -6318,8 +7574,8 @@ static void *queueRunner(void *param) | ||
6318 | 7574 | |
6319 | 7575 | if (fd < 0) { |
6320 | 7576 | ALOGE("%s() Failed to open AT channel %s (%s), retrying in %d.", |
6321 | - __func__, queueArgs->device_path, | |
6322 | - strerror(errno), TIMEOUT_SEARCH_FOR_TTY); | |
7577 | + __func__, queueArgs->device_path, | |
7578 | + strerror(errno), TIMEOUT_SEARCH_FOR_TTY); | |
6323 | 7579 | sleep(TIMEOUT_SEARCH_FOR_TTY); |
6324 | 7580 | /* Never returns. */ |
6325 | 7581 | } |
@@ -6339,18 +7595,12 @@ static void *queueRunner(void *param) | ||
6339 | 7595 | q = &s_requestQueue; |
6340 | 7596 | |
6341 | 7597 | if (initializeCommon()) { |
6342 | - ALOGE("%s() Failed to initialize channel!", __func__); | |
7598 | + ALOGE("%s() Failed to initialize common", __func__); | |
6343 | 7599 | at_close(); |
6344 | 7600 | continue; |
6345 | 7601 | } |
6346 | 7602 | |
6347 | 7603 | q->closed = 0; |
6348 | - if (initializeChannel()) { | |
6349 | - ALOGE("%s() Failed to initialize channel!", __func__); | |
6350 | - at_close(); | |
6351 | - continue; | |
6352 | - } | |
6353 | - | |
6354 | 7604 | at_make_default_channel(); |
6355 | 7605 | |
6356 | 7606 | ALOGE("%s() Looping the requestQueue!", __func__); |
@@ -6362,9 +7612,9 @@ static void *queueRunner(void *param) | ||
6362 | 7612 | |
6363 | 7613 | memset(&ts, 0, sizeof(ts)); |
6364 | 7614 | |
6365 | - if ((err = pthread_mutex_lock(&q->queueMutex)) != 0) | |
6366 | - ALOGE("%s() failed to take queue mutex: %s!", | |
6367 | - __func__, strerror(err)); | |
7615 | + if ((err = pthread_mutex_lock(&q->queueMutex)) != 0) | |
7616 | + ALOGE("%s() failed to take queue mutex: %s!", | |
7617 | + __func__, strerror(err)); | |
6368 | 7618 | |
6369 | 7619 | if (q->closed != 0) { |
6370 | 7620 | ALOGW("%s() AT Channel error, attempting to recover..", __func__); |
@@ -6374,10 +7624,10 @@ static void *queueRunner(void *param) | ||
6374 | 7624 | } |
6375 | 7625 | |
6376 | 7626 | while (q->closed == 0 && q->requestList == NULL && |
6377 | - q->eventList == NULL) { | |
7627 | + q->eventList == NULL) { | |
6378 | 7628 | if ((err = pthread_cond_wait(&q->cond, &q->queueMutex)) != 0) |
6379 | 7629 | ALOGE("%s() failed broadcast queue cond: %s!", |
6380 | - __func__, strerror(err)); | |
7630 | + __func__, strerror(err)); | |
6381 | 7631 | } |
6382 | 7632 | |
6383 | 7633 | /* eventList is prioritized, smallest abstime first. */ |
@@ -6386,13 +7636,13 @@ static void *queueRunner(void *param) | ||
6386 | 7636 | err = pthread_cond_timedwait(&q->cond, &q->queueMutex, &q->eventList->abstime); |
6387 | 7637 | if (err && err != ETIMEDOUT) |
6388 | 7638 | ALOGE("%s() timedwait returned unexpected error: %s", |
6389 | - __func__, strerror(err)); | |
7639 | + __func__, strerror(err)); | |
6390 | 7640 | } |
6391 | 7641 | |
6392 | 7642 | if (q->closed != 0) { |
6393 | 7643 | if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0) |
6394 | 7644 | ALOGE("%s(): Failed to release queue mutex: %s!", |
6395 | - __func__, strerror(err)); | |
7645 | + __func__, strerror(err)); | |
6396 | 7646 | continue; /* Catch the closed bit at the top of the loop. */ |
6397 | 7647 | } |
6398 | 7648 |
@@ -6402,7 +7652,7 @@ static void *queueRunner(void *param) | ||
6402 | 7652 | clock_gettime(CLOCK_MONOTONIC, &ts); |
6403 | 7653 | |
6404 | 7654 | if (q->eventList != NULL && |
6405 | - timespec_cmp(q->eventList->abstime, ts, < )) { | |
7655 | + timespec_cmp(q->eventList->abstime, ts, < )) { | |
6406 | 7656 | e = q->eventList; |
6407 | 7657 | q->eventList = e->next; |
6408 | 7658 | } |
@@ -6414,7 +7664,7 @@ static void *queueRunner(void *param) | ||
6414 | 7664 | |
6415 | 7665 | if ((err = pthread_mutex_unlock(&q->queueMutex)) != 0) |
6416 | 7666 | ALOGE("%s(): Failed to release queue mutex: %s!", |
6417 | - __func__, strerror(err)); | |
7667 | + __func__, strerror(err)); | |
6418 | 7668 | |
6419 | 7669 | if (e) { |
6420 | 7670 | e->eventCallback(e->param); |
@@ -6441,10 +7691,31 @@ const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, | ||
6441 | 7691 | int opt; |
6442 | 7692 | int port = -1; |
6443 | 7693 | char *loophost = NULL; |
6444 | - const char *device_path = NULL; | |
7694 | + const char *ctrl_path = NULL; | |
6445 | 7695 | struct queueArgs *queueArgs; |
6446 | 7696 | pthread_attr_t attr; |
6447 | 7697 | |
7698 | +#if 0 | |
7699 | + /* Logcat test */ | |
7700 | + char local_ip[64]; | |
7701 | + char dns1[64]; | |
7702 | + char dns2[64]; | |
7703 | + char gw[64]; | |
7704 | + struct timeval from; | |
7705 | + int rr; | |
7706 | + gettimeofday(&from,NULL); | |
7707 | + rr = get_pppd_info(&from,local_ip, dns1, dns2, gw); | |
7708 | + ALOGD("result: %d",rr); | |
7709 | +#endif | |
7710 | + | |
7711 | +#if 0 | |
7712 | + char * m = decodeUSSD("D9775D0E6286E7749050FEBECFD3EE33685A9ECFD36F37283D07BDCD201868297449CBED70DA9D769F414679B90C2287E96190FDCDAEB7CBA0F41C849BCD60B41C68297401"); | |
7713 | + ALOGD("Decoded msg: '%s'",m); | |
7714 | + free(m); | |
7715 | +#endif | |
7716 | + | |
7717 | + memset(&sAudioChannel, 0, sizeof(sAudioChannel)); | |
7718 | + | |
6448 | 7719 | s_rilenv = env; |
6449 | 7720 | |
6450 | 7721 | /* By default, use USB1 as audio channel */ |
@@ -6452,7 +7723,7 @@ const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, | ||
6452 | 7723 | |
6453 | 7724 | D("%s() entering...", __func__); |
6454 | 7725 | |
6455 | - while (-1 != (opt = getopt(argc, argv, "z:p:d:v:"))) { | |
7726 | + while (-1 != (opt = getopt(argc, argv, "z:n:i:p:d:v:"))) { | |
6456 | 7727 | switch (opt) { |
6457 | 7728 | case 'z': |
6458 | 7729 | loophost = optarg; |
@@ -6469,8 +7740,8 @@ const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, | ||
6469 | 7740 | break; |
6470 | 7741 | |
6471 | 7742 | case 'd': |
6472 | - device_path = optarg; | |
6473 | - D("%s() Opening tty device %s", __func__, device_path); | |
7743 | + ctrl_path = optarg; | |
7744 | + D("%s() Opening tty device %s", __func__, ctrl_path); | |
6474 | 7745 | break; |
6475 | 7746 | |
6476 | 7747 | case 'v': |
@@ -6478,21 +7749,30 @@ const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, | ||
6478 | 7749 | D("%s() Opening voice tty device %s", __func__, sAudioDevice); |
6479 | 7750 | break; |
6480 | 7751 | |
7752 | + case 'n': | |
7753 | + rndis_iface_dev = optarg; | |
7754 | + D("%s() Using network interface %s as primary data channel.", __func__, rndis_iface_dev); | |
7755 | + break; | |
7756 | + | |
7757 | + case 'i': | |
7758 | + ppp_iface_dev = optarg; | |
7759 | + D("%s() Using ppp interface %s as primary data channel.", __func__, ppp_iface_dev); | |
7760 | + break; | |
7761 | + | |
6481 | 7762 | default: |
6482 | 7763 | usage(argv[0]); |
6483 | 7764 | return NULL; |
6484 | 7765 | } |
6485 | 7766 | } |
6486 | 7767 | |
6487 | - if (port < 0 && device_path == NULL) { | |
7768 | + if (port < 0 && ctrl_path == NULL) { | |
6488 | 7769 | usage(argv[0]); |
6489 | 7770 | return NULL; |
6490 | 7771 | } |
6491 | 7772 | |
6492 | - queueArgs = (struct queueArgs*) malloc(sizeof(struct queueArgs)); | |
6493 | - memset(queueArgs, 0, sizeof(struct queueArgs)); | |
7773 | + queueArgs = (struct queueArgs*) calloc(1, sizeof(struct queueArgs)); | |
6494 | 7774 | |
6495 | - queueArgs->device_path = device_path; | |
7775 | + queueArgs->device_path = ctrl_path; | |
6496 | 7776 | queueArgs->port = port; |
6497 | 7777 | queueArgs->loophost = loophost; |
6498 | 7778 |
@@ -6510,15 +7790,17 @@ int main (int argc, char **argv) | ||
6510 | 7790 | int opt; |
6511 | 7791 | int port = -1; |
6512 | 7792 | char *loophost = NULL; |
6513 | - const char *device_path = NULL; | |
7793 | + const char *ctrl_path = NULL; | |
6514 | 7794 | struct queueArgs *queueArgs; |
6515 | 7795 | |
7796 | + memset(&sAudioChannel, 0, sizeof(sAudioChannel)); | |
7797 | + | |
6516 | 7798 | /* By default, use USB1 as audio channel */ |
6517 | 7799 | strcpy(sAudioDevice,"/dev/ttyUSB1"); |
6518 | 7800 | |
6519 | 7801 | D("%s() entering...", __func__); |
6520 | 7802 | |
6521 | - while (-1 != (opt = getopt(argc, argv, "z:p:d:v:"))) { | |
7803 | + while (-1 != (opt = getopt(argc, argv, "z:n:i:p:d:v:"))) { | |
6522 | 7804 | switch (opt) { |
6523 | 7805 | case 'z': |
6524 | 7806 | loophost = optarg; |
@@ -6535,8 +7817,8 @@ int main (int argc, char **argv) | ||
6535 | 7817 | break; |
6536 | 7818 | |
6537 | 7819 | case 'd': |
6538 | - device_path = optarg; | |
6539 | - D("%s() Opening tty device %s", __func__, device_path); | |
7820 | + ctrl_path = optarg; | |
7821 | + D("%s() Opening tty device %s", __func__, ctrl_path); | |
6540 | 7822 | break; |
6541 | 7823 | |
6542 | 7824 | case 'v': |
@@ -6544,21 +7826,30 @@ int main (int argc, char **argv) | ||
6544 | 7826 | D("%s() Opening voice tty device %s", __func__, sAudioDevice); |
6545 | 7827 | break; |
6546 | 7828 | |
7829 | + case 'n': | |
7830 | + rndis_iface_dev = optarg; | |
7831 | + D("%s() Using network interface %s as primary data channel.", __func__, rndis_iface_dev); | |
7832 | + break; | |
7833 | + | |
7834 | + case 'i': | |
7835 | + ppp_iface_dev = optarg; | |
7836 | + D("%s() Using ppp interface %s as primary data channel.", __func__, ppp_iface_dev); | |
7837 | + break; | |
7838 | + | |
6547 | 7839 | default: |
6548 | 7840 | usage(argv[0]); |
6549 | 7841 | return 0; |
6550 | 7842 | } |
6551 | 7843 | } |
6552 | 7844 | |
6553 | - if (port < 0 && device_path == NULL) { | |
7845 | + if (port < 0 && ctrl_path == NULL) { | |
6554 | 7846 | usage(argv[0]); |
6555 | 7847 | return 0; |
6556 | 7848 | } |
6557 | 7849 | |
6558 | - queueArgs = (struct queueArgs*) malloc(sizeof(struct queueArgs)); | |
6559 | - memset(queueArgs, 0, sizeof(struct queueArgs)); | |
7850 | + queueArgs = (struct queueArgs*) calloc(1, sizeof(struct queueArgs)); | |
6560 | 7851 | |
6561 | - queueArgs->device_path = device_path; | |
7852 | + queueArgs->device_path = ctrl_path; | |
6562 | 7853 | queueArgs->port = port; |
6563 | 7854 | queueArgs->loophost = loophost; |
6564 | 7855 |
@@ -78,9 +78,9 @@ char *getFirstElementValue(const char* document, | ||
78 | 78 | to be used when document contains many tags with same name. */ |
79 | 79 | if (remainingDocument != NULL) |
80 | 80 | *remainingDocument = end + strlen(elementEndTag); |
81 | + } | |
81 | 82 | } |
82 | 83 | } |
83 | - } | |
84 | 84 | return value; |
85 | 85 | } |
86 | 86 |
@@ -0,0 +1,188 @@ | ||
1 | +/* | |
2 | + * Copyright 2008, The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#include <stdio.h> | |
18 | +#include <stdlib.h> | |
19 | +#include <unistd.h> | |
20 | +#include <string.h> | |
21 | +#include <errno.h> | |
22 | + | |
23 | +#include <sys/socket.h> | |
24 | +#include <sys/select.h> | |
25 | +#include <sys/types.h> | |
26 | +#include <netinet/in.h> | |
27 | +#include <arpa/inet.h> | |
28 | + | |
29 | +#include <linux/if.h> | |
30 | +#include <linux/sockios.h> | |
31 | +#include <linux/route.h> | |
32 | +#include <linux/wireless.h> | |
33 | + | |
34 | +#define LOG_TAG "mbm-netutils" | |
35 | +#include <cutils/log.h> | |
36 | +#include <cutils/properties.h> | |
37 | + | |
38 | +static int ifc_ctl_sock = -1; | |
39 | + | |
40 | +static const char *ipaddr_to_string(in_addr_t addr) | |
41 | +{ | |
42 | + struct in_addr in_addr; | |
43 | + | |
44 | + in_addr.s_addr = addr; | |
45 | + return inet_ntoa(in_addr); | |
46 | +} | |
47 | + | |
48 | +int ifc_init(void) | |
49 | +{ | |
50 | + if (ifc_ctl_sock == -1) { | |
51 | + ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0); | |
52 | + if (ifc_ctl_sock < 0) | |
53 | + ALOGE("%s() socket() failed: %s", __func__, strerror(errno)); | |
54 | + } | |
55 | + return ifc_ctl_sock < 0 ? -1 : 0; | |
56 | +} | |
57 | + | |
58 | +void ifc_close(void) | |
59 | +{ | |
60 | + if (ifc_ctl_sock != -1) { | |
61 | + (void) close(ifc_ctl_sock); | |
62 | + ifc_ctl_sock = -1; | |
63 | + } | |
64 | +} | |
65 | + | |
66 | +static void ifc_init_ifr(const char *name, struct ifreq *ifr) | |
67 | +{ | |
68 | + memset(ifr, 0, sizeof(struct ifreq)); | |
69 | + strncpy(ifr->ifr_name, name, IFNAMSIZ); | |
70 | + ifr->ifr_name[IFNAMSIZ - 1] = 0; | |
71 | +} | |
72 | + | |
73 | +static int ifc_set_flags(const char *name, unsigned set, unsigned clr) | |
74 | +{ | |
75 | + struct ifreq ifr; | |
76 | + ifc_init_ifr(name, &ifr); | |
77 | + | |
78 | + if (ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) | |
79 | + return -1; | |
80 | + ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set; | |
81 | + return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr); | |
82 | +} | |
83 | + | |
84 | +int ifc_up(const char *name) | |
85 | +{ | |
86 | + return ifc_set_flags(name, IFF_UP | IFF_NOARP, 0); | |
87 | +} | |
88 | + | |
89 | +int ifc_down(const char *name) | |
90 | +{ | |
91 | + return ifc_set_flags(name, 0, IFF_UP); | |
92 | +} | |
93 | + | |
94 | +static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr) | |
95 | +{ | |
96 | + struct sockaddr_in *sin = (struct sockaddr_in *) sa; | |
97 | + sin->sin_family = AF_INET; | |
98 | + sin->sin_port = 0; | |
99 | + sin->sin_addr.s_addr = addr; | |
100 | +} | |
101 | + | |
102 | +int ifc_set_addr(const char *name, in_addr_t addr) | |
103 | +{ | |
104 | + struct ifreq ifr; | |
105 | + | |
106 | + ifc_init_ifr(name, &ifr); | |
107 | + init_sockaddr_in(&ifr.ifr_addr, addr); | |
108 | + | |
109 | + return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr); | |
110 | +} | |
111 | + | |
112 | +int ifc_set_mask(const char *name, in_addr_t mask) | |
113 | +{ | |
114 | + struct ifreq ifr; | |
115 | + | |
116 | + ifc_init_ifr(name, &ifr); | |
117 | + init_sockaddr_in(&ifr.ifr_addr, mask); | |
118 | + | |
119 | + return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr); | |
120 | +} | |
121 | + | |
122 | +int ifc_configure(const char *ifname, | |
123 | + in_addr_t address, | |
124 | + in_addr_t gateway) | |
125 | +{ | |
126 | + in_addr_t netmask = ~0; | |
127 | + (void) gateway; | |
128 | + | |
129 | + ifc_init(); | |
130 | + | |
131 | + if (ifc_up(ifname)) { | |
132 | + ALOGE("%s() Failed to turn on interface %s: %s", __func__, | |
133 | + ifname, | |
134 | + strerror(errno)); | |
135 | + ifc_close(); | |
136 | + return -1; | |
137 | + } | |
138 | + if (ifc_set_addr(ifname, address)) { | |
139 | + ALOGE("%s() Failed to set ipaddr %s: %s", __func__, | |
140 | + ipaddr_to_string(address), strerror(errno)); | |
141 | + ifc_down(ifname); | |
142 | + ifc_close(); | |
143 | + return -1; | |
144 | + } | |
145 | + if (ifc_set_mask(ifname, netmask)) { | |
146 | + ALOGE("%s() failed to set netmask %s: %s", __func__, | |
147 | + ipaddr_to_string(netmask), strerror(errno)); | |
148 | + ifc_down(ifname); | |
149 | + ifc_close(); | |
150 | + return -1; | |
151 | + } | |
152 | + | |
153 | + ifc_close(); | |
154 | + | |
155 | + return 0; | |
156 | +} | |
157 | + | |
158 | +int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *flags) | |
159 | +{ | |
160 | + struct ifreq ifr; | |
161 | + ifc_init_ifr(name, &ifr); | |
162 | + | |
163 | + if (addr != NULL) { | |
164 | + if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) { | |
165 | + *addr = 0; | |
166 | + } else { | |
167 | + *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; | |
168 | + } | |
169 | + } | |
170 | + | |
171 | + if (mask != NULL) { | |
172 | + if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) { | |
173 | + *mask = 0; | |
174 | + } else { | |
175 | + *mask = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; | |
176 | + } | |
177 | + } | |
178 | + | |
179 | + if (flags != NULL) { | |
180 | + if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) { | |
181 | + *flags = 0; | |
182 | + } else { | |
183 | + *flags = ifr.ifr_flags; | |
184 | + } | |
185 | + } | |
186 | + | |
187 | + return 0; | |
188 | +} |
@@ -0,0 +1,35 @@ | ||
1 | +/* | |
2 | + * Copyright 2008, The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#ifndef MBM_NET_UTILS_H | |
18 | +#define MBM_NET_UTILS_H 1 | |
19 | + | |
20 | +#include <arpa/inet.h> | |
21 | + | |
22 | +int ifc_init(void); | |
23 | +void ifc_close(void); | |
24 | +int ifc_up(const char *name); | |
25 | +int ifc_down(const char *name); | |
26 | +int ifc_set_addr(const char *name, in_addr_t addr); | |
27 | +int ifc_set_mask(const char *name, in_addr_t mask); | |
28 | +int ifc_add_host_route(const char *name); | |
29 | +int ifc_configure(const char *ifname, | |
30 | + in_addr_t address, | |
31 | + in_addr_t gateway); | |
32 | +int ifc_get_info(const char *name, in_addr_t *addr, | |
33 | + in_addr_t *mask, unsigned *flags); | |
34 | + | |
35 | +#endif |
@@ -183,10 +183,7 @@ static void *dispatchString(void *data, size_t datalen) | ||
183 | 183 | (void) data; (void) datalen; |
184 | 184 | assert(datalen == sizeof(char *)); |
185 | 185 | |
186 | - if (data) | |
187 | - return strdup((char *) data); | |
188 | - | |
189 | - return NULL; | |
186 | + return data ? strdup((char *) data) : NULL; | |
190 | 187 | } |
191 | 188 | |
192 | 189 | static void *dispatchStrings(void *data, size_t datalen) |
@@ -199,11 +196,9 @@ static void *dispatchStrings(void *data, size_t datalen) | ||
199 | 196 | assert((datalen % sizeof(char *)) == 0); |
200 | 197 | |
201 | 198 | ret = malloc(strCount * sizeof(char *)); |
202 | - memset(ret, 0, sizeof(char *) * strCount); | |
203 | 199 | |
204 | 200 | for (i = 0; i < strCount; i++) { |
205 | - if (a[i]) | |
206 | - ret[i] = strdup(a[i]); | |
201 | + ret[i] = a[i] ? strdup(a[i]) : 0; | |
207 | 202 | } |
208 | 203 | |
209 | 204 | return (void *) ret; |
@@ -211,20 +206,19 @@ static void *dispatchStrings(void *data, size_t datalen) | ||
211 | 206 | |
212 | 207 | static void *dispatchGsmBrSmsCnf(void *data, size_t datalen) |
213 | 208 | { |
214 | - RIL_GSM_BroadcastSmsConfigInfo **a = | |
215 | - (RIL_GSM_BroadcastSmsConfigInfo **) data; | |
209 | + RIL_GSM_BroadcastSmsConfigInfo **a; | |
216 | 210 | int count; |
217 | 211 | void **ret; |
218 | 212 | int i; |
219 | 213 | |
214 | + a = (RIL_GSM_BroadcastSmsConfigInfo **) data; | |
220 | 215 | count = datalen / sizeof(RIL_GSM_BroadcastSmsConfigInfo *); |
221 | 216 | |
222 | 217 | ret = malloc(count * sizeof(RIL_GSM_BroadcastSmsConfigInfo *)); |
223 | - memset(ret, 0, sizeof(*ret)); | |
224 | 218 | |
225 | 219 | for (i = 0; i < count; i++) { |
226 | - if (a[i]) | |
227 | - ret[i] = dispatchRaw(a[i], sizeof(RIL_GSM_BroadcastSmsConfigInfo)); | |
220 | + ret[i] = a[i] ? dispatchRaw(a[i], | |
221 | + sizeof(RIL_GSM_BroadcastSmsConfigInfo)) : 0; | |
228 | 222 | } |
229 | 223 | |
230 | 224 | return ret; |
@@ -248,10 +242,10 @@ static void freeDial(void *data) | ||
248 | 242 | { |
249 | 243 | RIL_Dial *d = data; |
250 | 244 | |
251 | - if (d->address) | |
245 | + if (d) { | |
252 | 246 | free(d->address); |
253 | - | |
254 | - free(d); | |
247 | + free(d); | |
248 | + } | |
255 | 249 | } |
256 | 250 | |
257 | 251 | static void freeStrings(void *data, size_t datalen) |
@@ -18,8 +18,7 @@ | ||
18 | 18 | |
19 | 19 | int hex2int(char c) |
20 | 20 | { |
21 | - if (c>'9') return c-'A'+10; | |
22 | - return c-'0'; | |
21 | + return (c>'9') ? c-'A'+10 : c-'0'; | |
23 | 22 | } |
24 | 23 | |
25 | 24 | int getbit(char *s,int b) |
@@ -28,8 +27,7 @@ int getbit(char *s,int b) | ||
28 | 27 | int bit=b%4; |
29 | 28 | |
30 | 29 | int data=hex2int(s[byte]); |
31 | - if (data&(1<<(3-bit))) return 1; | |
32 | - else return 0; | |
30 | + return (data&(1<<(3-bit))) ? 1 : 0; | |
33 | 31 | } |
34 | 32 | |
35 | 33 | const char hextable[17]="0123456789ABCDEF"; |
@@ -64,13 +62,12 @@ const char decode_table[17]=".1234567890*#..."; | ||
64 | 62 | |
65 | 63 | void decode_number(char *msg, int length, char *no) |
66 | 64 | { |
67 | - | |
68 | 65 | int ndigits=getbits(msg,2,8); |
69 | - int j,digit; | |
66 | + int j; | |
70 | 67 | |
71 | 68 | for (j=0;j<ndigits;j++) |
72 | - *no++=decode_table[getbits(msg,10+j*4,4)]; | |
73 | - *no=0; | |
69 | + *no++ = decode_table[getbits(msg,10+j*4,4)]; | |
70 | + *no = 0; | |
74 | 71 | } |
75 | 72 | |
76 | 73 | int encode_digit(int d) |
@@ -85,7 +82,6 @@ int encode_digit(int d) | ||
85 | 82 | int encode_number(char *msg, char *no) |
86 | 83 | { |
87 | 84 | unsigned int i; |
88 | - int digit; | |
89 | 85 | |
90 | 86 | setbits(msg, 0, 2, 0); |
91 | 87 | setbits(msg, 2, 8, strlen(no)); |
@@ -102,8 +98,8 @@ void get_code_and_length(char *msg, int *code, int *length) | ||
102 | 98 | |
103 | 99 | void decode_bearer_data(char *msg, int length, char *message, int *is_vm) |
104 | 100 | { |
105 | - int i=0,j; | |
106 | - int code,sublength; | |
101 | + int i = 0, j; | |
102 | + int code, sublength; | |
107 | 103 | |
108 | 104 | while (i<length) { |
109 | 105 | get_code_and_length(msg+i*2,&code,&sublength); |
@@ -140,32 +136,32 @@ void decode_bearer_data(char *msg, int length, char *message, int *is_vm) | ||
140 | 136 | |
141 | 137 | int encode_bearer_data(char *msg, char *data) |
142 | 138 | { |
143 | - int msgid=0; | |
139 | + int msgid = 0; | |
144 | 140 | unsigned int i; |
145 | - int b; | |
146 | - char *start=msg; | |
141 | + int b; | |
142 | + char *start = msg; | |
147 | 143 | |
148 | 144 | for (i=0;i<strlen(data);i++) |
149 | - msgid+=data[i]; | |
145 | + msgid += data[i]; | |
150 | 146 | |
151 | 147 | setbits(msg,0,8,0); // message id |
152 | 148 | setbits(msg,8,8,3); // 3 bytes |
153 | 149 | setbits(msg,16,4,2); // 2 means send |
154 | 150 | setbits(msg,20,16,msgid); // use message sum for id |
155 | - msg+=10; | |
151 | + msg += 10; | |
156 | 152 | setbits(msg,0,8,01); // user data |
157 | 153 | setbits(msg,16,5,02); // set encoding |
158 | 154 | setbits(msg,21,8,strlen(data)); // length |
159 | - b=29; | |
155 | + b = 29; | |
160 | 156 | for (i=0;i<strlen(data);i++) { |
161 | 157 | setbits(msg,b,7,data[i]); |
162 | - b=b+7; | |
158 | + b = b+7; | |
163 | 159 | } |
164 | 160 | setbits(msg,8,8,(b+7)/8-2); |
165 | - msg=msg+2*((b+7)/8); | |
161 | + msg = msg+2*((b+7)/8); | |
166 | 162 | setbits(msg,0,24,0x80100); |
167 | 163 | setbits(msg,24,24,0x0D0100); |
168 | - msg=msg+12; | |
164 | + msg = msg+12; | |
169 | 165 | return (msg-start)/2; |
170 | 166 | } |
171 | 167 |
@@ -242,7 +238,8 @@ char **cdma_to_gsmpdu(char *msg) | ||
242 | 238 | smsaddr.toa = 0xd0; |
243 | 239 | } |
244 | 240 | sms_timestamp_now(&smstime); |
245 | - SmsPDU *pdu=smspdu_create_deliver_utf8((const unsigned char *)message,strlen(message),&smsaddr,&smstime); | |
241 | + SmsPDU *pdu = smspdu_create_deliver_utf8( | |
242 | + (const unsigned char *)message, strlen(message), &smsaddr, &smstime); | |
246 | 243 | //hexpdu=malloc(512); |
247 | 244 | char *s=hexpdu; |
248 | 245 | while (*pdu) { |
@@ -260,26 +257,28 @@ char **cdma_to_gsmpdu(char *msg) | ||
260 | 257 | char *gsm_to_cdmapdu(char *msg) |
261 | 258 | { |
262 | 259 | char to[256]; |
263 | - char message[256]; | |
260 | + unsigned char message[256]; | |
264 | 261 | static char hexpdu[512]; |
265 | 262 | SmsAddressRec smsaddr; |
266 | - sms_address_from_str(&smsaddr,"000000",6); | |
263 | + int length; | |
264 | + | |
265 | + sms_address_from_str(&smsaddr, "000000", 6); | |
267 | 266 | |
268 | - SmsPDU pdu=smspdu_create_from_hex( msg, strlen(msg) ); | |
269 | - if (smspdu_get_receiver_address(pdu,&smsaddr)<0) { | |
267 | + SmsPDU pdu = smspdu_create_from_hex(msg, strlen(msg)); | |
268 | + if (smspdu_get_receiver_address(pdu, &smsaddr) < 0) { | |
270 | 269 | ALOGE("Error: no receiver address"); |
271 | - smspdu_get_sender_address(pdu,&smsaddr); | |
270 | + smspdu_get_sender_address(pdu, &smsaddr); | |
272 | 271 | } |
273 | - sms_address_to_str(&smsaddr,to,256); | |
272 | + sms_address_to_str(&smsaddr, to, 256); | |
274 | 273 | if (to[0]=='+') { // convert + to 00 otherwise international sms doesn't work |
275 | - memmove(to+1,to,255); | |
274 | + memmove(to+1, to, 255); | |
276 | 275 | to[0]='0'; |
277 | 276 | to[1]='0'; |
278 | 277 | } |
279 | - int length=smspdu_get_text_message(pdu, message, 256); | |
280 | - message[length]=0; | |
278 | + length = smspdu_get_text_message(pdu, message, 256); | |
279 | + message[length] = 0; | |
281 | 280 | smspdu_free(pdu); |
282 | - ALOGD("GSM Message:%s To:%s\n",message,to); | |
283 | - encode_cdma_sms(hexpdu,to,message); | |
281 | + ALOGD("GSM Message:%s To:%s\n", message, to); | |
282 | + encode_cdma_sms(hexpdu, to, (char *)message); | |
284 | 283 | return hexpdu; |
285 | 284 | } |
@@ -165,14 +165,14 @@ gsm_rope_add_timestamp( GsmRope rope, const SmsTimeStampRec* ts ) | ||
165 | 165 | int |
166 | 166 | sms_address_to_str( SmsAddress address, char* str, int strlen ) |
167 | 167 | { |
168 | - bytes_t data = address->data; | |
169 | - if(address->toa == 0x91) | |
170 | - *str++='+'; | |
171 | 168 | int i; |
172 | 169 | char c; |
170 | + bytes_t data = address->data; | |
171 | + if (address->toa == 0x91) | |
172 | + *str++='+'; | |
173 | 173 | for (i=0;i<address->len;i++) { |
174 | 174 | c=data[i/2]; |
175 | - if(i&1) c=c>>4; | |
175 | + if (i&1) c=c>>4; | |
176 | 176 | *str++='0'+(c&15); |
177 | 177 | } |
178 | 178 | *str=0; |
@@ -206,8 +206,8 @@ sms_address_from_str( SmsAddress address, const char* src, int srclen ) | ||
206 | 206 | while (src < end) { |
207 | 207 | int c = *src++ - '0'; |
208 | 208 | |
209 | - if ( (unsigned)c >= 10 || | |
210 | - data >= address->data + sizeof(address->data) ) | |
209 | + if ((unsigned)c >= 10 || | |
210 | + data >= address->data + sizeof(address->data)) | |
211 | 211 | goto Fail; |
212 | 212 | |
213 | 213 | data[0] |= c << shift; |
@@ -231,7 +231,7 @@ Fail: | ||
231 | 231 | int |
232 | 232 | sms_address_from_bytes( SmsAddress address, const unsigned char* buf, int buflen ) |
233 | 233 | { |
234 | - int len = sizeof(address->data), num_digits; | |
234 | + unsigned int len = sizeof(address->data), num_digits; | |
235 | 235 | |
236 | 236 | if (buflen < 2) |
237 | 237 | return -1; |
@@ -357,8 +357,8 @@ sms_get_sc_address( cbytes_t *pcur, | ||
357 | 357 | int result = -1; |
358 | 358 | |
359 | 359 | if (cur < end) { |
360 | - int len = cur[0]; | |
361 | - int dlen, adjust = 0; | |
360 | + unsigned int len = cur[0]; | |
361 | + unsigned int dlen, adjust = 0; | |
362 | 362 | |
363 | 363 | cur += 1; |
364 | 364 |
@@ -438,7 +438,7 @@ sms_get_address( cbytes_t *pcur, | ||
438 | 438 | { |
439 | 439 | cbytes_t cur = *pcur; |
440 | 440 | int result = -1; |
441 | - int len, dlen; | |
441 | + unsigned int len, dlen; | |
442 | 442 | |
443 | 443 | if (cur >= end) |
444 | 444 | goto Exit; |
@@ -683,8 +683,7 @@ sms_get_text_utf8( cbytes_t *pcur, | ||
683 | 683 | len = *cur++; |
684 | 684 | |
685 | 685 | /* skip user data header if any */ |
686 | - if ( hasUDH ) | |
687 | - { | |
686 | + if ( hasUDH ) { | |
688 | 687 | int hlen; |
689 | 688 | |
690 | 689 | if (cur >= end) |
@@ -709,11 +708,10 @@ sms_get_text_utf8( cbytes_t *pcur, | ||
709 | 708 | if (coding == SMS_CODING_SCHEME_GSM7) { |
710 | 709 | int count = utf8_from_gsm7( cur, 0, len, NULL ); |
711 | 710 | |
712 | - if (rope != NULL) | |
713 | - { | |
711 | + if (rope != NULL) { | |
714 | 712 | bytes_t dst = gsm_rope_reserve( rope, count ); |
715 | - if(hasUDH && dst) | |
716 | - *dst++=(*cur++)>>1; | |
713 | + if(hasUDH && dst) | |
714 | + *dst++=(*cur++)>>1; | |
717 | 715 | if (dst != NULL) |
718 | 716 | utf8_from_gsm7( cur, 0, len, dst ); |
719 | 717 | } |
@@ -721,8 +719,7 @@ sms_get_text_utf8( cbytes_t *pcur, | ||
721 | 719 | } else if (coding == SMS_CODING_SCHEME_UCS2) { |
722 | 720 | int count = ucs2_to_utf8( cur, len/2, NULL ); |
723 | 721 | |
724 | - if (rope != NULL) | |
725 | - { | |
722 | + if (rope != NULL) { | |
726 | 723 | bytes_t dst = gsm_rope_reserve( rope, count ); |
727 | 724 | if (dst != NULL) |
728 | 725 | ucs2_to_utf8( cur, len/2, dst ); |
@@ -1065,7 +1062,7 @@ smspdu_create_from_hex( const char* hex, int hexlen ) | ||
1065 | 1062 | goto Exit; |
1066 | 1063 | } |
1067 | 1064 | |
1068 | - gsm_hex_to_bytes( hex, hexlen, p->base ); | |
1065 | + gsm_hex_to_bytes((cbytes_t) hex, hexlen, p->base ); | |
1069 | 1066 | p->end = p->base + (hexlen+1)/2; |
1070 | 1067 | |
1071 | 1068 | data = p->base; |