1 /* $Id$ */
2 /* Copyright (c) 2011-2016 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Phone */
4 /* This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */
15 /* FIXME:
16 * - implement priorities again
17 * - parse messages from within +CMGL already
18 * - add MCT callbacks/buttons to change the SIM code (via a helper in phone.c)
19 * - implement new contacts */
20
21
22
23 #ifdef __linux__
24 # include <sys/file.h>
25 #endif
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <time.h>
33 #include <ctype.h>
34 #include <termios.h>
35 #include <errno.h>
36 #include <libgen.h>
37 #include <glib.h>
38 #include <System.h>
39 #include <Phone/modem.h>
40 #include "hayes/channel.h"
41 #include "hayes/command.h"
42 #include "hayes/common.h"
43 #include "hayes/pdu.h"
44 #include "hayes/quirks.h"
45 #include "hayes.h"
46
47 #define max(a, b) ((a) > (b) ? (a) : (b))
48 #define min(a, b) ((a) < (b) ? (a) : (b))
49
50
51 /* Hayes */
52 /* private */
53 /* types */
54 typedef struct _ModemPlugin
55 {
56 ModemPluginHelper * helper;
57
58 unsigned int retry;
59
60 /* modem */
61 HayesChannel channel;
62 } Hayes;
63
64 #ifdef DEBUG
65 static const char * _hayes_command_status[HCS_COUNT] =
66 {
67 "HCS_UNKNOWN", "HCS_QUEUED", "HCS_UNKNOWN", "HCS_ACTIVE", "HCS_TIMEOUT",
68 "HCS_ERROR", "HCS_SUCCESS"
69 };
70 #endif
71
72 typedef struct _HayesRequestContactList
73 {
74 unsigned int from;
75 unsigned int to;
76 } HayesRequestContactList;
77
78 typedef struct _HayesRequestMessageData
79 {
80 unsigned int id;
81 ModemMessageFolder folder;
82 ModemMessageStatus status;
83 } HayesRequestMessageData;
84
85 typedef struct _HayesRequestHandler
86 {
87 unsigned int type;
88 char const * attention;
89 HayesCommandCallback callback;
90 } HayesRequestHandler;
91
92 typedef struct _HayesCodeHandler
93 {
94 char const * code;
95 void (*callback)(HayesChannel * channel, char const * answer);
96 } HayesCodeHandler;
97
98
99 /* constants */
100 enum
101 {
102 HAYES_REQUEST_ALIVE = MODEM_REQUEST_COUNT,
103 HAYES_REQUEST_CALL_WAITING_UNSOLLICITED_DISABLE,
104 HAYES_REQUEST_CALL_WAITING_UNSOLLICITED_ENABLE,
105 HAYES_REQUEST_CONNECTED_LINE_DISABLE,
106 HAYES_REQUEST_CONNECTED_LINE_ENABLE,
107 HAYES_REQUEST_CONTACT_LIST,
108 HAYES_REQUEST_EXTENDED_ERRORS,
109 HAYES_REQUEST_EXTENDED_RING_REPORTS,
110 HAYES_REQUEST_FUNCTIONAL,
111 HAYES_REQUEST_FUNCTIONAL_DISABLE,
112 HAYES_REQUEST_FUNCTIONAL_ENABLE,
113 HAYES_REQUEST_FUNCTIONAL_ENABLE_RESET,
114 HAYES_REQUEST_GPRS_ATTACHED,
115 HAYES_REQUEST_LOCAL_ECHO_DISABLE,
116 HAYES_REQUEST_LOCAL_ECHO_ENABLE,
117 HAYES_REQUEST_MESSAGE_FORMAT_PDU,
118 HAYES_REQUEST_MESSAGE_LIST_INBOX_READ,
119 HAYES_REQUEST_MESSAGE_LIST_INBOX_UNREAD,
120 HAYES_REQUEST_MESSAGE_LIST_SENT_READ,
121 HAYES_REQUEST_MESSAGE_LIST_SENT_UNREAD,
122 HAYES_REQUEST_MESSAGE_UNSOLLICITED_DISABLE,
123 HAYES_REQUEST_MESSAGE_UNSOLLICITED_ENABLE,
124 HAYES_REQUEST_MODEL,
125 HAYES_REQUEST_OPERATOR,
126 HAYES_REQUEST_OPERATOR_FORMAT_SHORT,
127 HAYES_REQUEST_OPERATOR_FORMAT_LONG,
128 HAYES_REQUEST_OPERATOR_FORMAT_NUMERIC,
129 HAYES_REQUEST_PHONE_ACTIVE,
130 HAYES_REQUEST_REGISTRATION,
131 HAYES_REQUEST_REGISTRATION_AUTOMATIC,
132 HAYES_REQUEST_REGISTRATION_DISABLED,
133 HAYES_REQUEST_REGISTRATION_UNSOLLICITED_DISABLE,
134 HAYES_REQUEST_REGISTRATION_UNSOLLICITED_ENABLE,
135 HAYES_REQUEST_SERIAL_NUMBER,
136 HAYES_REQUEST_SIM_PIN_VALID,
137 HAYES_REQUEST_SUBSCRIBER_IDENTITY,
138 HAYES_REQUEST_SUPPLEMENTARY_SERVICE_DATA_CANCEL,
139 HAYES_REQUEST_SUPPLEMENTARY_SERVICE_DATA_ENABLE,
140 HAYES_REQUEST_SUPPLEMENTARY_SERVICE_DATA_DISABLE,
141 HAYES_REQUEST_VENDOR,
142 HAYES_REQUEST_VERBOSE_DISABLE,
143 HAYES_REQUEST_VERBOSE_ENABLE,
144 HAYES_REQUEST_VERSION
145 };
146
147
148 /* prototypes */
149 /* plug-in */
150 static ModemPlugin * _hayes_init(ModemPluginHelper * helper);
151 static void _hayes_destroy(Hayes * hayes);
152 static int _hayes_start(Hayes * hayes, unsigned int retry);
153 static int _hayes_stop(Hayes * hayes);
154 static int _hayes_request(Hayes * hayes, ModemRequest * request);
155 static int _hayes_trigger(Hayes * hayes, ModemEventType event);
156
157 /* accessors */
158 static void _hayes_set_mode(Hayes * hayes, HayesChannel * channel,
159 HayesChannelMode mode);
160
161 /* useful */
162 /* conversions */
163 static unsigned char _hayes_convert_gsm_to_iso(unsigned char c);
164 static void _hayes_convert_gsm_string_to_iso(char * str);
165 static unsigned char _hayes_convert_iso_to_gsm(unsigned char c);
166 static void _hayes_convert_iso_string_to_gsm(char * str);
167
168 /* messages */
169 static char * _hayes_message_to_pdu(HayesChannel * channel, char const * number,
170 ModemMessageEncoding encoding, size_t length,
171 char const * content);
172
173 /* logging */
174 static void _hayes_log(Hayes * hayes, HayesChannel * channel,
175 char const * prefix, char const * buf, size_t cnt);
176
177 /* parser */
178 static int _hayes_parse(Hayes * hayes, HayesChannel * channel);
179 static int _hayes_parse_pdu(Hayes * hayes, HayesChannel * channel);
180 static int _hayes_parse_trigger(HayesChannel * channel, char const * answer,
181 HayesCommand * command);
182
183 /* queue */
184 static int _hayes_queue_command(Hayes * hayes, HayesChannel * channel,
185 HayesCommand * command);
186 #if 0 /* XXX no longer used */
187 static int _hayes_queue_command_full(Hayes * hayes,
188 char const * attention, HayesCommandCallback callback);
189 #endif
190 static int _hayes_queue_push(Hayes * hayes, HayesChannel * channel);
191
192 /* requests */
193 static int _hayes_request_channel(Hayes * hayes, HayesChannel * channel,
194 ModemRequest * request, void * data);
195 static int _hayes_request_type(Hayes * hayes, HayesChannel * channel,
196 ModemRequestType type);
197
198 /* reset */
199 static void _hayes_reset(Hayes * hayes);
200
201 /* callbacks */
202 static gboolean _on_channel_authenticate(gpointer data);
203 static gboolean _on_channel_reset(gpointer data);
204 static gboolean _on_channel_timeout(gpointer data);
205 static gboolean _on_queue_timeout(gpointer data);
206 static gboolean _on_reset_settle(gpointer data);
207 static gboolean _on_reset_settle2(gpointer data);
208 static gboolean _on_watch_can_read(GIOChannel * source, GIOCondition condition,
209 gpointer data);
210 static gboolean _on_watch_can_read_ppp(GIOChannel * source,
211 GIOCondition condition, gpointer data);
212 static gboolean _on_watch_can_write(GIOChannel * source, GIOCondition condition,
213 gpointer data);
214 static gboolean _on_watch_can_write_ppp(GIOChannel * source,
215 GIOCondition condition, gpointer data);
216
217 static HayesCommandStatus _on_request_authenticate(HayesCommand * command,
218 HayesCommandStatus status, HayesChannel * channel);
219 static HayesCommandStatus _on_request_battery_level(HayesCommand * command,
220 HayesCommandStatus status, HayesChannel * channel);
221 static HayesCommandStatus _on_request_call(HayesCommand * command,
222 HayesCommandStatus status, HayesChannel * channel);
223 static HayesCommandStatus _on_request_call_incoming(HayesCommand * command,
224 HayesCommandStatus status, HayesChannel * channel);
225 static HayesCommandStatus _on_request_call_outgoing(HayesCommand * command,
226 HayesCommandStatus status, HayesChannel * channel);
227 static HayesCommandStatus _on_request_call_status(HayesCommand * command,
228 HayesCommandStatus status, HayesChannel * channel);
229 static HayesCommandStatus _on_request_contact_delete(HayesCommand * command,
230 HayesCommandStatus status, HayesChannel * channel);
231 static HayesCommandStatus _on_request_contact_list(HayesCommand * command,
232 HayesCommandStatus status, HayesChannel * channel);
233 static HayesCommandStatus _on_request_functional(HayesCommand * command,
234 HayesCommandStatus status, HayesChannel * channel);
235 static HayesCommandStatus _on_request_functional_enable(HayesCommand * command,
236 HayesCommandStatus status, HayesChannel * channel);
237 static HayesCommandStatus _on_request_functional_enable_reset(
238 HayesCommand * command, HayesCommandStatus status,
239 HayesChannel * channel);
240 static HayesCommandStatus _on_request_generic(HayesCommand * command,
241 HayesCommandStatus status, HayesChannel * channel);
242 static HayesCommandStatus _on_request_message(HayesCommand * command,
243 HayesCommandStatus status, HayesChannel * channel);
244 static HayesCommandStatus _on_request_message_delete(HayesCommand * command,
245 HayesCommandStatus status, HayesChannel * channel);
246 static HayesCommandStatus _on_request_message_list(HayesCommand * command,
247 HayesCommandStatus status, HayesChannel * channel);
248 static HayesCommandStatus _on_request_message_send(HayesCommand * command,
249 HayesCommandStatus status, HayesChannel * channel);
250 static HayesCommandStatus _on_request_model(HayesCommand * command,
251 HayesCommandStatus status, HayesChannel * channel);
252 static HayesCommandStatus _on_request_registration(HayesCommand * command,
253 HayesCommandStatus status, HayesChannel * channel);
254 static HayesCommandStatus _on_request_registration_automatic(
255 HayesCommand * command, HayesCommandStatus status,
256 HayesChannel * channel);
257 static HayesCommandStatus _on_request_registration_disabled(
258 HayesCommand * command, HayesCommandStatus status,
259 HayesChannel * channel);
260 static HayesCommandStatus _on_request_sim_pin_valid(HayesCommand * command,
261 HayesCommandStatus status, HayesChannel * channel);
262 static HayesCommandStatus _on_request_unsupported(HayesCommand * command,
263 HayesCommandStatus status, HayesChannel * channel);
264
265 static void _on_code_call_error(HayesChannel * channel, char const * answer);
266 static void _on_code_cbc(HayesChannel * channel, char const * answer);
267 static void _on_code_cfun(HayesChannel * channel, char const * answer);
268 static void _on_code_cgatt(HayesChannel * channel, char const * answer);
269 static void _on_code_cgmi(HayesChannel * channel, char const * answer);
270 static void _on_code_cgmm(HayesChannel * channel, char const * answer);
271 static void _on_code_cgmr(HayesChannel * channel, char const * answer);
272 static void _on_code_cgsn(HayesChannel * channel, char const * answer);
273 static void _on_code_cimi(HayesChannel * channel, char const * answer);
274 static void _on_code_clip(HayesChannel * channel, char const * answer);
275 static void _on_code_cme_error(HayesChannel * channel, char const * answer);
276 static void _on_code_cmgl(HayesChannel * channel, char const * answer);
277 static void _on_code_cmgr(HayesChannel * channel, char const * answer);
278 static void _on_code_cmgs(HayesChannel * channel, char const * answer);
279 static void _on_code_cms_error(HayesChannel * channel, char const * answer);
280 static void _on_code_cmti(HayesChannel * channel, char const * answer);
281 static void _on_code_connect(HayesChannel * channel, char const * answer);
282 static void _on_code_colp(HayesChannel * channel, char const * answer);
283 static void _on_code_cops(HayesChannel * channel, char const * answer);
284 static void _on_code_cpas(HayesChannel * channel, char const * answer);
285 static void _on_code_cpbr(HayesChannel * channel, char const * answer);
286 static void _on_code_cpin(HayesChannel * channel, char const * answer);
287 static void _on_code_creg(HayesChannel * channel, char const * answer);
288 static void _on_code_cring(HayesChannel * channel, char const * answer);
289 static void _on_code_csq(HayesChannel * channel, char const * answer);
290 static void _on_code_cusd(HayesChannel * channel, char const * answer);
291 static void _on_code_ext_error(HayesChannel * channel, char const * answer);
292
293 /* helpers */
294 static int _is_ussd_code(char const * number);
295
296
297 /* variables */
298 static ModemConfig _hayes_config[] =
299 {
300 { "device", "Device", MCT_FILENAME },
301 { "baudrate", "Baudrate", MCT_UINT32 },
302 { "hwflow", "Hardware flow control",MCT_BOOLEAN },
303 { NULL, "Advanced", MCT_SUBSECTION },
304 { "logfile", "Log file", MCT_FILENAME },
305 { NULL, NULL, MCT_NONE },
306 };
307
308 static struct
309 {
310 unsigned char gsm;
311 unsigned char iso;
312 } _hayes_gsm_iso[] =
313 {
314 { '\0', '@' },
315 { 0x01, 163 }, /* £ */
316 { 0x02, '$' },
317 { 0x03, 165 }, /* ¥ */
318 { 0x04, 232 }, /* è */
319 { 0x05, 233 }, /* é */
320 { 0x06, 249 }, /* ù */
321 { 0x07, 236 }, /* ì */
322 { 0x08, 242 }, /* ò */
323 { 0x09, 199 }, /* Ç */
324 { 0x0b, 216 }, /* Ø */
325 { 0x0c, 248 }, /* ø */
326 { 0x0e, 197 }, /* Å */
327 { 0x0f, 229 }, /* å */
328 { 0x10, ' ' }, /* XXX delta */
329 { 0x11, '_' },
330 { 0x12, ' ' }, /* XXX phi */
331 { 0x13, ' ' }, /* XXX gamma */
332 { 0x14, ' ' }, /* XXX lambda */
333 { 0x15, ' ' }, /* XXX omega */
334 { 0x16, ' ' }, /* XXX pi */
335 { 0x17, ' ' }, /* XXX psi */
336 { 0x18, ' ' }, /* XXX sigma */
337 { 0x19, ' ' }, /* XXX theta */
338 { 0x1a, ' ' }, /* XXX xi */
339 { 0x1b, ' ' }, /* FIXME escape */
340 { 0x1c, 198 },
341 { 0x1d, 230 },
342 { 0x1e, 223 },
343 { 0x1f, 201 },
344 { 0x24, 164 }, /* $ */
345 { 0x40, 161 }, /* @ */
346 { 0x5b, 196 }, /* [ */
347 { 0x5c, 214 }, /* \ */
348 { 0x5d, 209 }, /* ] */
349 { 0x5e, 220 }, /* ^ */
350 { 0x5f, 167 }, /* _ */
351 { 0x60, 191 }, /* ` */
352 { 0x7b, 228 }, /* { */
353 { 0x7c, 246 }, /* | */
354 { 0x7d, 241 }, /* } */
355 { 0x7e, 252 }, /* ~ */
356 { 0x7f, 224 }
357 };
358
359 static HayesRequestHandler _hayes_request_handlers[] =
360 {
361 { HAYES_REQUEST_ALIVE, "AT",
362 _on_request_generic },
363 { HAYES_REQUEST_CALL_WAITING_UNSOLLICITED_DISABLE,"AT+CCWA=1",
364 _on_request_generic },
365 { HAYES_REQUEST_CALL_WAITING_UNSOLLICITED_ENABLE,"AT+CCWA=1",
366 _on_request_generic },
367 { HAYES_REQUEST_CONNECTED_LINE_DISABLE, "AT+COLP=0",
368 _on_request_generic },
369 { HAYES_REQUEST_CONNECTED_LINE_ENABLE, "AT+COLP=1",
370 _on_request_generic },
371 { HAYES_REQUEST_CONTACT_LIST, NULL,
372 _on_request_generic },
373 { HAYES_REQUEST_EXTENDED_ERRORS, "AT+CMEE=1",
374 _on_request_generic },
375 { HAYES_REQUEST_EXTENDED_RING_REPORTS, "AT+CRC=1",
376 _on_request_generic },
377 { HAYES_REQUEST_FUNCTIONAL, "AT+CFUN?",
378 _on_request_functional },
379 { HAYES_REQUEST_FUNCTIONAL_DISABLE, "AT+CFUN=0",
380 _on_request_generic },
381 { HAYES_REQUEST_FUNCTIONAL_ENABLE, "AT+CFUN=1",
382 _on_request_functional_enable },
383 { HAYES_REQUEST_FUNCTIONAL_ENABLE_RESET, "AT+CFUN=1,1",
384 _on_request_functional_enable_reset },
385 { HAYES_REQUEST_GPRS_ATTACHED, "AT+CGATT?",
386 _on_request_generic },
387 { HAYES_REQUEST_LOCAL_ECHO_DISABLE, "ATE0",
388 _on_request_generic },
389 { HAYES_REQUEST_LOCAL_ECHO_ENABLE, "ATE1",
390 _on_request_generic },
391 { HAYES_REQUEST_MESSAGE_FORMAT_PDU, "AT+CMGF=0",
392 _on_request_generic },
393 { HAYES_REQUEST_MESSAGE_LIST_INBOX_UNREAD, "AT+CMGL=0",
394 _on_request_message_list },
395 { HAYES_REQUEST_MESSAGE_LIST_INBOX_READ, "AT+CMGL=1",
396 _on_request_message_list },
397 { HAYES_REQUEST_MESSAGE_LIST_SENT_UNREAD, "AT+CMGL=2",
398 _on_request_message_list },
399 { HAYES_REQUEST_MESSAGE_LIST_SENT_READ, "AT+CMGL=3",
400 _on_request_message_list },
401 { HAYES_REQUEST_MESSAGE_UNSOLLICITED_DISABLE, "AT+CNMI=0",
402 _on_request_generic },
403 { HAYES_REQUEST_MESSAGE_UNSOLLICITED_ENABLE, "AT+CNMI=1,1",
404 _on_request_generic }, /* XXX report error? */
405 { HAYES_REQUEST_MODEL, "AT+CGMM",
406 _on_request_model },
407 { HAYES_REQUEST_OPERATOR, "AT+COPS?",
408 _on_request_generic },
409 { HAYES_REQUEST_OPERATOR_FORMAT_LONG, "AT+COPS=3,0",
410 _on_request_registration },
411 { HAYES_REQUEST_OPERATOR_FORMAT_NUMERIC, "AT+COPS=3,2",
412 _on_request_registration },
413 { HAYES_REQUEST_OPERATOR_FORMAT_SHORT, "AT+COPS=3,1",
414 _on_request_registration },
415 { HAYES_REQUEST_PHONE_ACTIVE, "AT+CPAS",
416 _on_request_call },
417 { HAYES_REQUEST_REGISTRATION, "AT+CREG?",
418 _on_request_generic },
419 { HAYES_REQUEST_REGISTRATION_AUTOMATIC, "AT+COPS=0",
420 _on_request_registration_automatic },
421 { HAYES_REQUEST_REGISTRATION_DISABLED, "AT+COPS=2",
422 _on_request_registration_disabled },
423 { HAYES_REQUEST_REGISTRATION_UNSOLLICITED_DISABLE,"AT+CREG=0",
424 _on_request_generic },
425 { HAYES_REQUEST_REGISTRATION_UNSOLLICITED_ENABLE,"AT+CREG=2",
426 _on_request_registration },
427 { HAYES_REQUEST_SERIAL_NUMBER, "AT+CGSN",
428 _on_request_generic },
429 { HAYES_REQUEST_SIM_PIN_VALID, "AT+CPIN?",
430 _on_request_sim_pin_valid },
431 { HAYES_REQUEST_SUBSCRIBER_IDENTITY, "AT+CIMI",
432 _on_request_generic },
433 { HAYES_REQUEST_SUPPLEMENTARY_SERVICE_DATA_CANCEL,"AT+CUSD=2",
434 _on_request_generic },
435 { HAYES_REQUEST_SUPPLEMENTARY_SERVICE_DATA_DISABLE,"AT+CUSD=0",
436 _on_request_generic },
437 { HAYES_REQUEST_SUPPLEMENTARY_SERVICE_DATA_ENABLE,"AT+CUSD=1",
438 _on_request_generic },
439 { HAYES_REQUEST_VENDOR, "AT+CGMI",
440 _on_request_generic },
441 { HAYES_REQUEST_VERBOSE_DISABLE, "ATV0",
442 _on_request_generic },
443 { HAYES_REQUEST_VERBOSE_ENABLE, "ATV1",
444 _on_request_generic },
445 { HAYES_REQUEST_VERSION, "AT+CGMR",
446 _on_request_generic },
447 { MODEM_REQUEST_AUTHENTICATE, NULL,
448 _on_request_authenticate },
449 { MODEM_REQUEST_BATTERY_LEVEL, "AT+CBC",
450 _on_request_battery_level },
451 { MODEM_REQUEST_CALL, NULL,
452 _on_request_call_outgoing },
453 { MODEM_REQUEST_CALL_ANSWER, "ATA",
454 _on_request_call_incoming },
455 { MODEM_REQUEST_CALL_HANGUP, NULL,
456 _on_request_call_status },
457 { MODEM_REQUEST_CALL_PRESENTATION, NULL,
458 _on_request_generic },
459 { MODEM_REQUEST_CONNECTIVITY, NULL,
460 _on_request_generic },
461 { MODEM_REQUEST_CONTACT_DELETE, NULL,
462 _on_request_contact_delete },
463 { MODEM_REQUEST_CONTACT_EDIT, NULL,
464 _on_request_contact_list },
465 { MODEM_REQUEST_CONTACT_LIST, "AT+CPBR=?",
466 _on_request_generic },
467 { MODEM_REQUEST_CONTACT_NEW, NULL,
468 _on_request_contact_list },
469 { MODEM_REQUEST_DTMF_SEND, NULL,
470 _on_request_generic },
471 { MODEM_REQUEST_MESSAGE, NULL,
472 _on_request_message },
473 { MODEM_REQUEST_MESSAGE_DELETE, NULL,
474 _on_request_message_delete },
475 { MODEM_REQUEST_MESSAGE_LIST, NULL,
476 _on_request_message_list },
477 { MODEM_REQUEST_MESSAGE_SEND, NULL,
478 _on_request_message_send },
479 { MODEM_REQUEST_PASSWORD_SET, NULL,
480 _on_request_generic },
481 { MODEM_REQUEST_REGISTRATION, NULL,
482 _on_request_registration },
483 { MODEM_REQUEST_SIGNAL_LEVEL, "AT+CSQ",
484 _on_request_generic },
485 { MODEM_REQUEST_UNSUPPORTED, NULL,
486 _on_request_unsupported }
487 };
488
489 static HayesCodeHandler _hayes_code_handlers[] =
490 {
491 { "+CBC", _on_code_cbc },
492 { "+CFUN", _on_code_cfun },
493 { "+CGATT", _on_code_cgatt },
494 { "+CGMI", _on_code_cgmi },
495 { "+CGMM", _on_code_cgmm },
496 { "+CGMR", _on_code_cgmr },
497 { "+CGSN", _on_code_cgsn },
498 { "+CIMI", _on_code_cimi },
499 { "+CLIP", _on_code_clip },
500 { "+CME ERROR", _on_code_cme_error },
501 { "+CMGL", _on_code_cmgl },
502 { "+CMGR", _on_code_cmgr },
503 { "+CMGS", _on_code_cmgs },
504 { "+CMS ERROR", _on_code_cms_error },
505 { "+CMTI", _on_code_cmti },
506 { "+COLP", _on_code_colp },
507 { "+COPS", _on_code_cops },
508 { "+CPAS", _on_code_cpas },
509 { "+CPBR", _on_code_cpbr },
510 { "+CPIN", _on_code_cpin },
511 { "+CREG", _on_code_creg },
512 { "+CRING", _on_code_cring },
513 { "+CSQ", _on_code_csq },
514 { "+CUSD", _on_code_cusd },
515 { "+EXT ERROR", _on_code_ext_error },
516 { "BUSY", _on_code_call_error },
517 { "CONNECT", _on_code_connect },
518 { "NO CARRIER", _on_code_call_error },
519 { "NO DIALTONE",_on_code_call_error },
520 { "RING", _on_code_cring }
521 };
522
523
524 /* public */
525 /* variables */
526 ModemPluginDefinition plugin =
527 {
528 "Hayes",
529 "phone",
530 _hayes_config,
531 _hayes_init,
532 _hayes_destroy,
533 _hayes_start,
534 _hayes_stop,
535 _hayes_request,
536 _hayes_trigger
537 };
538
539
540 /* private */
541 /* plug-in */
542 /* functions */
_hayes_init(ModemPluginHelper * helper)543 static ModemPlugin * _hayes_init(ModemPluginHelper * helper)
544 {
545 Hayes * hayes;
546
547 if((hayes = object_new(sizeof(*hayes))) == NULL)
548 return NULL;
549 memset(hayes, 0, sizeof(*hayes));
550 hayes->helper = helper;
551 hayeschannel_init(&hayes->channel, hayes);
552 return hayes;
553 }
554
555
556 /* hayes_destroy */
_hayes_destroy(Hayes * hayes)557 static void _hayes_destroy(Hayes * hayes)
558 {
559 _hayes_stop(hayes);
560 hayeschannel_destroy(&hayes->channel);
561 object_delete(hayes);
562 }
563
564
565 /* hayes_request */
_hayes_request(Hayes * hayes,ModemRequest * request)566 static int _hayes_request(Hayes * hayes, ModemRequest * request)
567 {
568 return _hayes_request_channel(hayes, &hayes->channel, request, NULL);
569 }
570
571
572 /* hayes_start */
573 static int _start_is_started(Hayes * hayes);
574
_hayes_start(Hayes * hayes,unsigned int retry)575 static int _hayes_start(Hayes * hayes, unsigned int retry)
576 {
577 hayes->retry = retry;
578 if(_start_is_started(hayes))
579 return 0;
580 if(hayes->channel.source != 0)
581 g_source_remove(hayes->channel.source);
582 hayes->channel.source = g_idle_add(_on_channel_reset, &hayes->channel);
583 return 0;
584 }
585
_start_is_started(Hayes * hayes)586 static int _start_is_started(Hayes * hayes)
587 {
588 if(hayes->channel.source != 0)
589 return 1;
590 if(hayeschannel_is_started(&hayes->channel))
591 return 1;
592 return 0;
593 }
594
595
596 /* hayes_stop */
_hayes_stop(Hayes * hayes)597 static int _hayes_stop(Hayes * hayes)
598 {
599 HayesChannel * channel = &hayes->channel;
600 ModemEvent * event;
601
602 hayescommon_source_reset(&channel->source);
603 hayeschannel_stop(channel);
604 /* report disconnection if already connected */
605 event = &channel->events[MODEM_EVENT_TYPE_CONNECTION];
606 if(event->connection.connected)
607 {
608 event->connection.connected = 0;
609 event->connection.in = 0;
610 event->connection.out = 0;
611 hayes->helper->event(hayes->helper->modem, event);
612 }
613 /* reset battery information */
614 event = &channel->events[MODEM_EVENT_TYPE_BATTERY_LEVEL];
615 if(event->battery_level.status != MODEM_BATTERY_STATUS_UNKNOWN)
616 {
617 event->battery_level.status = MODEM_BATTERY_STATUS_UNKNOWN;
618 event->battery_level.level = 0.0 / 0.0;
619 event->battery_level.charging = 0;
620 hayes->helper->event(hayes->helper->modem, event);
621 }
622 return 0;
623 }
624
625
626 /* hayes_trigger */
_hayes_trigger(Hayes * hayes,ModemEventType event)627 static int _hayes_trigger(Hayes * hayes, ModemEventType event)
628 {
629 int ret = 0;
630 HayesChannel * channel = &hayes->channel;
631 ModemEvent * e;
632
633 #ifdef DEBUG
634 fprintf(stderr, "DEBUG: %s(%u)\n", __func__, event);
635 #endif
636 switch(event)
637 {
638 case MODEM_EVENT_TYPE_BATTERY_LEVEL: /* use the existing data */
639 case MODEM_EVENT_TYPE_CALL:
640 case MODEM_EVENT_TYPE_CONNECTION:
641 case MODEM_EVENT_TYPE_STATUS:
642 e = &channel->events[event];
643 hayes->helper->event(hayes->helper->modem, e);
644 break;
645 case MODEM_EVENT_TYPE_AUTHENTICATION:
646 return _hayes_request_type(hayes, channel,
647 HAYES_REQUEST_SIM_PIN_VALID);
648 case MODEM_EVENT_TYPE_CONTACT:
649 return _hayes_request_type(hayes, channel,
650 MODEM_REQUEST_CONTACT_LIST);
651 case MODEM_EVENT_TYPE_MESSAGE:
652 return _hayes_request_type(hayes, channel,
653 MODEM_REQUEST_MESSAGE_LIST);
654 case MODEM_EVENT_TYPE_MODEL:
655 ret |= _hayes_request_type(hayes, channel,
656 HAYES_REQUEST_VENDOR);
657 ret |= _hayes_request_type(hayes, channel,
658 HAYES_REQUEST_VERSION);
659 ret |= _hayes_request_type(hayes, channel,
660 HAYES_REQUEST_SERIAL_NUMBER);
661 ret |= _hayes_request_type(hayes, channel,
662 HAYES_REQUEST_SUBSCRIBER_IDENTITY);
663 ret |= _hayes_request_type(hayes, channel,
664 HAYES_REQUEST_MODEL);
665 break;
666 case MODEM_EVENT_TYPE_REGISTRATION:
667 e = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
668 if(e->registration.status
669 == MODEM_REGISTRATION_STATUS_UNKNOWN)
670 ret |= _hayes_request_type(hayes, channel,
671 HAYES_REQUEST_REGISTRATION);
672 else
673 hayes->helper->event(hayes->helper->modem, e);
674 break;
675 case MODEM_EVENT_TYPE_CONTACT_DELETED: /* do not make sense */
676 case MODEM_EVENT_TYPE_ERROR:
677 case MODEM_EVENT_TYPE_NOTIFICATION:
678 case MODEM_EVENT_TYPE_MESSAGE_DELETED:
679 case MODEM_EVENT_TYPE_MESSAGE_SENT:
680 ret = -1;
681 break;
682 }
683 return ret;
684 }
685
686
687 /* accessors */
688 /* hayes_set_mode */
_hayes_set_mode(Hayes * hayes,HayesChannel * channel,HayesChannelMode mode)689 static void _hayes_set_mode(Hayes * hayes, HayesChannel * channel,
690 HayesChannelMode mode)
691 {
692 ModemEvent * event;
693
694 #ifdef DEBUG
695 fprintf(stderr, "DEBUG: %s(%u)\n", __func__, mode);
696 #endif
697 if(channel->mode == mode)
698 return;
699 switch(channel->mode)
700 {
701 case HAYESCHANNEL_MODE_INIT:
702 case HAYESCHANNEL_MODE_COMMAND:
703 case HAYESCHANNEL_MODE_PDU:
704 break; /* nothing to do */
705 case HAYESCHANNEL_MODE_DATA:
706 hayescommon_source_reset(&channel->rd_ppp_source);
707 hayescommon_source_reset(&channel->wr_ppp_source);
708 /* reset registration media */
709 event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
710 free(channel->registration_media);
711 channel->registration_media = NULL;
712 event->registration.media = NULL;
713 /* reset modem */
714 _hayes_reset(hayes);
715 break;
716 }
717 switch(mode)
718 {
719 case HAYESCHANNEL_MODE_INIT:
720 case HAYESCHANNEL_MODE_COMMAND:
721 case HAYESCHANNEL_MODE_PDU:
722 break; /* nothing to do */
723 case HAYESCHANNEL_MODE_DATA:
724 /* report GPRS registration */
725 event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
726 free(channel->registration_media);
727 channel->registration_media = NULL;
728 event->registration.media = "GPRS";
729 hayes->helper->event(hayes->helper->modem, event);
730 break;
731 }
732 channel->mode = mode;
733 }
734
735
736 /* conversions */
737 /* hayes_convert_gsm_to_iso */
_hayes_convert_gsm_to_iso(unsigned char c)738 static unsigned char _hayes_convert_gsm_to_iso(unsigned char c)
739 {
740 size_t i;
741
742 for(i = 0; i < sizeof(_hayes_gsm_iso) / sizeof(*_hayes_gsm_iso); i++)
743 if(_hayes_gsm_iso[i].gsm == c)
744 return _hayes_gsm_iso[i].iso;
745 return c;
746 }
747
748
749 /* hayes_convert_gsm_string_to_iso */
_hayes_convert_gsm_string_to_iso(char * str)750 static void _hayes_convert_gsm_string_to_iso(char * str)
751 {
752 unsigned char * ustr = (unsigned char *)str;
753 size_t i;
754
755 for(i = 0; str[i] != '\0'; i++)
756 ustr[i] = _hayes_convert_gsm_to_iso(ustr[i]);
757 }
758
759
760 /* hayes_convert_iso_to_gsm */
_hayes_convert_iso_to_gsm(unsigned char c)761 static unsigned char _hayes_convert_iso_to_gsm(unsigned char c)
762 {
763 size_t i;
764
765 for(i = 0; i < sizeof(_hayes_gsm_iso) / sizeof(*_hayes_gsm_iso); i++)
766 if(_hayes_gsm_iso[i].iso == c)
767 return _hayes_gsm_iso[i].gsm;
768 return c;
769 }
770
771
772 /* hayes_convert_iso_string_to_gsm */
_hayes_convert_iso_string_to_gsm(char * str)773 static void _hayes_convert_iso_string_to_gsm(char * str)
774 {
775 unsigned char * ustr = (unsigned char *)str;
776 size_t i;
777
778 for(i = 0; str[i] != '\0'; i++)
779 ustr[i] = _hayes_convert_iso_to_gsm(ustr[i]);
780 }
781
782
783 /* logging */
784 /* hayes_log */
_hayes_log(Hayes * hayes,HayesChannel * channel,char const * prefix,char const * buf,size_t cnt)785 static void _hayes_log(Hayes * hayes, HayesChannel * channel,
786 char const * prefix, char const * buf, size_t cnt)
787 {
788 ModemPluginHelper * helper = hayes->helper;
789
790 if(channel->fp == NULL)
791 return;
792 if(fprintf(channel->fp, "\n%s", (prefix != NULL) ? prefix : "") == EOF
793 || fwrite(buf, sizeof(*buf), cnt, channel->fp) < cnt)
794 {
795 helper->error(NULL, strerror(errno), 1);
796 fclose(channel->fp);
797 channel->fp = NULL;
798 }
799 }
800
801
802 /* messages */
803 /* hayes_message_to_pdu */
_hayes_message_to_pdu(HayesChannel * channel,char const * number,ModemMessageEncoding encoding,size_t length,char const * content)804 static char * _hayes_message_to_pdu(HayesChannel * channel, char const * number,
805 ModemMessageEncoding encoding, size_t length,
806 char const * content)
807 {
808 unsigned int flags = 0;
809
810 flags |= hayeschannel_has_quirks(channel, HAYES_QUIRK_WANT_SMSC_IN_PDU)
811 ? HAYESPDU_FLAG_WANT_SMSC : 0;
812 return hayespdu_encode(number, encoding, length, content, flags);
813 }
814
815
816 /* parser */
817 /* hayes_parse */
818 static int _parse_do(Hayes * hayes, HayesChannel * channel);
819
_hayes_parse(Hayes * hayes,HayesChannel * channel)820 static int _hayes_parse(Hayes * hayes, HayesChannel * channel)
821 {
822 int ret = 0;
823 size_t i;
824 char * p;
825
826 #ifdef DEBUG
827 fprintf(stderr, "DEBUG: %s() cnt=%lu\n", __func__,
828 (unsigned long)channel->rd_buf_cnt);
829 #endif
830 for(i = 0; i < channel->rd_buf_cnt;)
831 {
832 if(channel->rd_buf[i] == '\r')
833 {
834 if(i + 1 < channel->rd_buf_cnt
835 && channel->rd_buf[i + 1] == '\n')
836 channel->rd_buf[i++] = '\0';
837 }
838 else if(channel->rd_buf[i] != '\n')
839 {
840 i++;
841 continue;
842 }
843 channel->rd_buf[i++] = '\0';
844 if(channel->rd_buf[0] != '\0')
845 ret |= _parse_do(hayes, channel);
846 channel->rd_buf_cnt -= i;
847 memmove(channel->rd_buf, &channel->rd_buf[i],
848 channel->rd_buf_cnt);
849 i = 0;
850 }
851 if((p = realloc(channel->rd_buf, channel->rd_buf_cnt)) != NULL)
852 /* we can ignore errors... */
853 channel->rd_buf = p;
854 else if(channel->rd_buf_cnt == 0)
855 /* ...except when it's not one */
856 channel->rd_buf = NULL;
857 return ret;
858 }
859
_parse_do(Hayes * hayes,HayesChannel * channel)860 static int _parse_do(Hayes * hayes, HayesChannel * channel)
861 {
862 char const * line = channel->rd_buf;
863 HayesCommand * command = (channel->queue != NULL) ? channel->queue->data
864 : NULL;
865 HayesCommandStatus status;
866
867 if(command == NULL || hayes_command_get_status(command) != HCS_ACTIVE)
868 /* this was most likely unsollicited */
869 return _hayes_parse_trigger(channel, line, NULL);
870 _hayes_parse_trigger(channel, line, command);
871 if(hayes_command_answer_append(command, line) != 0)
872 return -1;
873 if((status = hayes_command_get_status(command)) == HCS_ACTIVE)
874 hayes_command_callback(command);
875 /* unqueue if complete */
876 if(hayes_command_is_complete(command))
877 {
878 hayeschannel_queue_pop(channel);
879 _hayes_queue_push(hayes, channel);
880 }
881 return 0;
882 }
883
884
885 /* hayes_parse_pdu */
886 static int _parse_pdu_resume(Hayes * hayes, HayesChannel * channel);
887 static int _parse_pdu_send(Hayes * hayes, HayesChannel * channel);
888
_hayes_parse_pdu(Hayes * hayes,HayesChannel * channel)889 static int _hayes_parse_pdu(Hayes * hayes, HayesChannel * channel)
890 {
891 size_t i;
892
893 #ifdef DEBUG
894 fprintf(stderr, "DEBUG: %s() cnt=%lu\n", __func__,
895 (unsigned long)channel->rd_buf_cnt);
896 for(i = 0; i < channel->rd_buf_cnt; i++)
897 fprintf(stderr, " %02x", channel->rd_buf[i]);
898 fputc('\n', stderr);
899 #endif
900 for(i = 0; i < channel->rd_buf_cnt;)
901 {
902 if(channel->rd_buf[i] == '\r'
903 || channel->rd_buf[i] == '\n')
904 {
905 /* ignore carriage returns */
906 i++;
907 continue;
908 }
909 /* look for the PDU prompt */
910 if(channel->rd_buf[i] != '>')
911 return _parse_pdu_resume(hayes, channel);
912 if(i + 1 >= channel->rd_buf_cnt)
913 /* we need more data */
914 break;
915 if(channel->rd_buf[++i] != ' ')
916 return _parse_pdu_resume(hayes, channel);
917 _parse_pdu_send(hayes, channel);
918 i++;
919 break;
920 }
921 channel->rd_buf_cnt -= i;
922 memmove(channel->rd_buf, &channel->rd_buf[i], channel->rd_buf_cnt);
923 return 0;
924 }
925
_parse_pdu_resume(Hayes * hayes,HayesChannel * channel)926 static int _parse_pdu_resume(Hayes * hayes, HayesChannel * channel)
927 {
928 char eop = 0x1a;
929
930 #ifdef DEBUG
931 fprintf(stderr, "DEBUG: %s()\n", __func__);
932 #endif
933 /* XXX check for errors and report them */
934 hayeschannel_queue_data(channel, &eop, sizeof(eop));
935 if(channel->channel != NULL && channel->wr_source == 0)
936 channel->wr_source = g_io_add_watch(channel->channel, G_IO_OUT,
937 _on_watch_can_write, channel);
938 _hayes_set_mode(hayes, channel, HAYESCHANNEL_MODE_COMMAND);
939 return 0;
940 }
941
_parse_pdu_send(Hayes * hayes,HayesChannel * channel)942 static int _parse_pdu_send(Hayes * hayes, HayesChannel * channel)
943 {
944 HayesCommand * command = (channel->queue != NULL)
945 ? channel->queue->data : NULL;
946 char * pdu;
947
948 #ifdef DEBUG
949 fprintf(stderr, "DEBUG: %s()\n", __func__);
950 #endif
951 if(command != NULL && hayes_command_get_status(command) == HCS_ACTIVE
952 && (pdu = hayes_command_get_data(command)) != NULL)
953 {
954 /* XXX check for errors and report them */
955 hayeschannel_queue_data(channel, pdu, strlen(pdu));
956 free(pdu);
957 hayes_command_set_data(command, NULL);
958 }
959 return _parse_pdu_resume(hayes, channel);
960 }
961
962
963 /* hayes_parse_trigger */
_hayes_parse_trigger(HayesChannel * channel,char const * answer,HayesCommand * command)964 static int _hayes_parse_trigger(HayesChannel * channel, char const * answer,
965 HayesCommand * command)
966 {
967 size_t i;
968 const size_t count = sizeof(_hayes_code_handlers)
969 / sizeof(*_hayes_code_handlers);
970 size_t len;
971 HayesCodeHandler * hch;
972 char const * p;
973 int j;
974
975 #ifdef DEBUG
976 fprintf(stderr, "DEBUG: %s(\"%s\", command)\n", __func__, answer);
977 #endif
978 /* if the handler is obvious return directly */
979 for(i = 0; i < count; i++)
980 {
981 hch = &_hayes_code_handlers[i];
982 len = strlen(hch->code);
983 if(strncmp(hch->code, answer, len) != 0)
984 continue;
985 if(answer[len] == ':')
986 {
987 if(answer[++len] == ' ') /* skip the optional space */
988 len++;
989 }
990 else if(answer[len] != '\0')
991 continue;
992 hch->callback(channel, &answer[len]);
993 return 0;
994 }
995 /* if the answer has no prefix choose it from the command issued */
996 if(command == NULL
997 || (p = hayes_command_get_attention(command)) == NULL
998 || strncmp(p, "AT", 2) != 0)
999 return 0;
1000 for(i = 0; i < count; i++)
1001 {
1002 hch = &_hayes_code_handlers[i];
1003 len = strlen(hch->code);
1004 if(strncmp(hch->code, &p[2], len) != 0
1005 || isalnum((j = p[2 + len])))
1006 continue;
1007 hch->callback(channel, answer);
1008 return 0;
1009 }
1010 return 0;
1011 }
1012
1013
1014 /* queue */
1015 /* hayes_queue_command */
_hayes_queue_command(Hayes * hayes,HayesChannel * channel,HayesCommand * command)1016 static int _hayes_queue_command(Hayes * hayes, HayesChannel * channel,
1017 HayesCommand * command)
1018 {
1019 GSList * queue;
1020
1021 switch(channel->mode)
1022 {
1023 case HAYESCHANNEL_MODE_INIT:
1024 /* ignore commands besides initialization */
1025 if(hayes_command_get_priority(command)
1026 != HCP_IMMEDIATE)
1027 return -1;
1028 case HAYESCHANNEL_MODE_COMMAND:
1029 case HAYESCHANNEL_MODE_DATA:
1030 case HAYESCHANNEL_MODE_PDU:
1031 if(hayes_command_set_status(command, HCS_QUEUED)
1032 != HCS_QUEUED)
1033 return -1;
1034 queue = channel->queue;
1035 channel->queue = g_slist_append(channel->queue,
1036 command);
1037 if(queue == NULL)
1038 _hayes_queue_push(hayes, channel);
1039 break;
1040 }
1041 return 0;
1042 }
1043
1044
1045 #if 0 /* XXX no longer used */
1046 /* hayes_queue_command_full */
1047 static int _hayes_queue_command_full(Hayes * hayes,
1048 char const * attention, HayesCommandCallback callback)
1049 {
1050 HayesCommand * command;
1051
1052 #ifdef DEBUG
1053 fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, attention);
1054 #endif
1055 if((command = hayes_command_new(attention)) == NULL)
1056 return -hayes->helper->error(hayes->helper->modem,
1057 error_get(NULL), 1);
1058 hayes_command_set_callback(command, callback, hayes);
1059 if(_hayes_queue_command(hayes, command) != 0)
1060 {
1061 hayes_command_delete(command);
1062 return -1;
1063 }
1064 return 0;
1065 }
1066 #endif
1067
1068
1069 /* hayes_queue_push */
1070 static int _queue_push_do(Hayes * hayes, HayesChannel * channel);
1071
_hayes_queue_push(Hayes * hayes,HayesChannel * channel)1072 static int _hayes_queue_push(Hayes * hayes, HayesChannel * channel)
1073 {
1074 while(_queue_push_do(hayes, channel) != 0);
1075 return 0;
1076 }
1077
_queue_push_do(Hayes * hayes,HayesChannel * channel)1078 static int _queue_push_do(Hayes * hayes, HayesChannel * channel)
1079 {
1080 HayesCommand * command;
1081 char const * prefix = "";
1082 char const * attention;
1083 const char suffix[] = "\r\n";
1084 size_t size;
1085 char * buf;
1086 guint timeout;
1087
1088 if(channel->queue == NULL) /* nothing to send */
1089 return 0;
1090 command = channel->queue->data;
1091 if(channel->mode == HAYESCHANNEL_MODE_DATA)
1092 #if 0 /* FIXME does not seem to work (see ATS2, ATS12) */
1093 prefix = "+++\r\n";
1094 #else
1095 return 0; /* XXX keep commands in the queue in DATA mode */
1096 #endif
1097 if(hayes_command_set_status(command, HCS_PENDING) != HCS_PENDING)
1098 {
1099 /* no longer push the command */
1100 hayeschannel_queue_pop(channel);
1101 return -1;
1102 }
1103 attention = hayes_command_get_attention(command);
1104 #ifdef DEBUG
1105 fprintf(stderr, "DEBUG: %s() pushing \"%s\"\n", __func__, attention);
1106 #endif
1107 size = strlen(prefix) + strlen(attention) + sizeof(suffix);
1108 if((buf = malloc(size)) == NULL
1109 || snprintf(buf, size, "%s%s%s", prefix, attention,
1110 suffix) != (int)size - 1
1111 || hayeschannel_queue_data(channel, buf, size - 1) != 0)
1112 {
1113 free(buf);
1114 hayes_command_set_status(command, HCS_ERROR);
1115 hayeschannel_queue_pop(channel);
1116 return -hayes->helper->error(hayes->helper->modem, strerror(
1117 errno), 1);
1118 }
1119 free(buf);
1120 if(channel->channel != NULL && channel->wr_source == 0)
1121 channel->wr_source = g_io_add_watch(channel->channel, G_IO_OUT,
1122 _on_watch_can_write, channel);
1123 hayescommon_source_reset(&channel->timeout);
1124 if((timeout = hayes_command_get_timeout(command)) != 0)
1125 channel->timeout = g_timeout_add(timeout, _on_channel_timeout,
1126 channel);
1127 return 0;
1128 }
1129
1130
1131 /* hayes_request_channel */
1132 static char * _request_attention(Hayes * hayes, HayesChannel * channel,
1133 ModemRequest * request, void ** data);
1134 static char * _request_attention_apn(char const * protocol, char const * apn);
1135 static char * _request_attention_call(HayesChannel * channel,
1136 ModemRequest * request);
1137 static char * _request_attention_call_ussd(ModemRequest * request);
1138 static char * _request_attention_call_hangup(Hayes * hayes,
1139 HayesChannel * channel);
1140 static char * _request_attention_connectivity(Hayes * hayes,
1141 HayesChannel * channel, unsigned int enabled);
1142 static char * _request_attention_contact_delete(HayesChannel * channel,
1143 unsigned int id);
1144 static char * _request_attention_contact_edit(unsigned int id,
1145 char const * name, char const * number);
1146 static char * _request_attention_contact_list(ModemRequest * request);
1147 static char * _request_attention_contact_new(char const * name,
1148 char const * number);
1149 static char * _request_attention_dtmf_send(ModemRequest * request);
1150 static char * _request_attention_gprs(Hayes * hayes, HayesChannel * channel,
1151 char const * username, char const * password);
1152 static char * _request_attention_message(unsigned int id);
1153 static char * _request_attention_message_delete(HayesChannel * channel,
1154 unsigned int id);
1155 static char * _request_attention_message_list(Hayes * hayes,
1156 HayesChannel * channel);
1157 static char * _request_attention_message_send(Hayes * hayes,
1158 HayesChannel * channel, char const * number,
1159 ModemMessageEncoding encoding, size_t length,
1160 char const * content, void ** data);
1161 static char * _request_attention_password_set(Hayes * hayes, char const * name,
1162 char const * oldpassword, char const * newpassword);
1163 static char * _request_attention_registration(Hayes * hayes,
1164 HayesChannel * channel, ModemRegistrationMode mode,
1165 char const * _operator);
1166 static char * _request_attention_sim_pin(Hayes * hayes, HayesChannel * channel,
1167 char const * password);
1168 static char * _request_attention_sim_puk(Hayes * hayes, HayesChannel * channel,
1169 char const * password);
1170 static char * _request_attention_unsupported(Hayes * hayes,
1171 ModemRequest * request);
1172 static int _request_channel_handler(Hayes * hayes, HayesChannel * channel,
1173 ModemRequest * request, void * data,
1174 HayesRequestHandler * handler);
1175
_hayes_request_channel(Hayes * hayes,HayesChannel * channel,ModemRequest * request,void * data)1176 static int _hayes_request_channel(Hayes * hayes, HayesChannel * channel,
1177 ModemRequest * request, void * data)
1178 {
1179 unsigned int type = request->type;
1180 size_t i;
1181 const size_t count = sizeof(_hayes_request_handlers)
1182 / sizeof(*_hayes_request_handlers);
1183
1184 #ifdef DEBUG
1185 fprintf(stderr, "DEBUG: %s(%u)\n", __func__, (request != NULL)
1186 ? request->type : (unsigned)-1);
1187 #endif
1188 if(request == NULL)
1189 return -1;
1190 if(hayeschannel_has_quirks(channel, HAYES_QUIRK_CONNECTED_LINE_DISABLED)
1191 && type == HAYES_REQUEST_CONNECTED_LINE_ENABLE)
1192 request->type = HAYES_REQUEST_CONNECTED_LINE_DISABLE;
1193 for(i = 0; i < count; i++)
1194 if(_hayes_request_handlers[i].type == request->type)
1195 break;
1196 if(i == count)
1197 #ifdef DEBUG
1198 return -hayes->helper->error(hayes->helper->modem,
1199 "Unable to handle request", 1);
1200 #else
1201 return -hayes->helper->error(NULL, "Unable to handle request",
1202 1);
1203 #endif
1204 return _request_channel_handler(hayes, channel, request, data,
1205 &_hayes_request_handlers[i]);
1206 }
1207
_request_channel_handler(Hayes * hayes,HayesChannel * channel,ModemRequest * request,void * data,HayesRequestHandler * handler)1208 static int _request_channel_handler(Hayes * hayes, HayesChannel * channel,
1209 ModemRequest * request, void * data,
1210 HayesRequestHandler * handler)
1211 {
1212 HayesCommand * command;
1213 char const * attention;
1214 char * p = NULL;
1215
1216 if((attention = handler->attention) == NULL)
1217 {
1218 if((p = _request_attention(hayes, channel, request, &data))
1219 == NULL)
1220 return 0; /* XXX errors should not be ignored */
1221 attention = p;
1222 }
1223 /* XXX using _hayes_queue_command_full() was more elegant */
1224 command = hayes_command_new(attention);
1225 free(p);
1226 if(command == NULL)
1227 return -1;
1228 hayes_command_set_callback(command, handler->callback, channel);
1229 if(_hayes_queue_command(hayes, channel, command) != 0)
1230 {
1231 hayes_command_delete(command);
1232 return -1;
1233 }
1234 if(data != NULL)
1235 hayes_command_set_data(command, data);
1236 return 0;
1237 }
1238
_request_attention(Hayes * hayes,HayesChannel * channel,ModemRequest * request,void ** data)1239 static char * _request_attention(Hayes * hayes, HayesChannel * channel,
1240 ModemRequest * request, void ** data)
1241 {
1242 unsigned int type = request->type;
1243 char buf[32];
1244
1245 switch(type)
1246 {
1247 case HAYES_REQUEST_CONTACT_LIST:
1248 return _request_attention_contact_list(request);
1249 case MODEM_REQUEST_AUTHENTICATE:
1250 if(strcmp(request->authenticate.name, "APN") == 0)
1251 return _request_attention_apn(
1252 request->authenticate.username,
1253 request->authenticate.password);
1254 if(strcmp(request->authenticate.name, "GPRS") == 0)
1255 return _request_attention_gprs(hayes, channel,
1256 request->authenticate.username,
1257 request->authenticate.password);
1258 if(strcmp(request->authenticate.name, "SIM PIN") == 0)
1259 return _request_attention_sim_pin(hayes,
1260 channel,
1261 request->authenticate.password);
1262 if(strcmp(request->authenticate.name, "SIM PUK") == 0)
1263 return _request_attention_sim_puk(hayes,
1264 channel,
1265 request->authenticate.password);
1266 break;
1267 case MODEM_REQUEST_CALL:
1268 if(request->call.call_type == MODEM_CALL_TYPE_VOICE
1269 && _is_ussd_code(request->call.number))
1270 return _request_attention_call_ussd(request);
1271 return _request_attention_call(channel, request);
1272 case MODEM_REQUEST_CALL_HANGUP:
1273 return _request_attention_call_hangup(hayes, channel);
1274 case MODEM_REQUEST_CALL_PRESENTATION:
1275 snprintf(buf, sizeof(buf), "%s%u", "AT+CLIP=",
1276 request->call_presentation.enabled
1277 ? 1 : 0);
1278 return strdup(buf);
1279 case MODEM_REQUEST_CONNECTIVITY:
1280 return _request_attention_connectivity(hayes, channel,
1281 request->connectivity.enabled);
1282 case MODEM_REQUEST_CONTACT_DELETE:
1283 return _request_attention_contact_delete(channel,
1284 request->contact_delete.id);
1285 case MODEM_REQUEST_CONTACT_EDIT:
1286 return _request_attention_contact_edit(
1287 request->contact_edit.id,
1288 request->contact_edit.name,
1289 request->contact_edit.number);
1290 case MODEM_REQUEST_CONTACT_NEW:
1291 return _request_attention_contact_new(
1292 request->contact_new.name,
1293 request->contact_new.number);
1294 case MODEM_REQUEST_DTMF_SEND:
1295 return _request_attention_dtmf_send(request);
1296 case MODEM_REQUEST_MESSAGE:
1297 return _request_attention_message(request->message.id);
1298 case MODEM_REQUEST_MESSAGE_LIST:
1299 return _request_attention_message_list(hayes, channel);
1300 case MODEM_REQUEST_MESSAGE_DELETE:
1301 return _request_attention_message_delete(channel,
1302 request->message_delete.id);
1303 case MODEM_REQUEST_MESSAGE_SEND:
1304 return _request_attention_message_send(hayes, channel,
1305 request->message_send.number,
1306 request->message_send.encoding,
1307 request->message_send.length,
1308 request->message_send.content, data);
1309 case MODEM_REQUEST_PASSWORD_SET:
1310 return _request_attention_password_set(hayes,
1311 request->password_set.name,
1312 request->password_set.oldpassword,
1313 request->password_set.newpassword);
1314 case MODEM_REQUEST_REGISTRATION:
1315 return _request_attention_registration(hayes, channel,
1316 request->registration.mode,
1317 request->registration._operator);
1318 case MODEM_REQUEST_UNSUPPORTED:
1319 return _request_attention_unsupported(hayes, request);
1320 default:
1321 break;
1322 }
1323 return NULL;
1324 }
1325
_request_attention_apn(char const * protocol,char const * apn)1326 static char * _request_attention_apn(char const * protocol, char const * apn)
1327 {
1328 char * ret;
1329 const char cmd[] = "AT+CGDCONT=1,";
1330 size_t len;
1331
1332 if(protocol == NULL || apn == NULL)
1333 return NULL;
1334 len = sizeof(cmd) + strlen(protocol) + 2 + strlen(apn) + 3;
1335 if((ret = malloc(len)) == NULL)
1336 return NULL;
1337 snprintf(ret, len, "%s\"%s\",\"%s\"", cmd, protocol, apn);
1338 return ret;
1339 }
1340
_request_attention_call(HayesChannel * channel,ModemRequest * request)1341 static char * _request_attention_call(HayesChannel * channel,
1342 ModemRequest * request)
1343 {
1344 char * ret;
1345 char const * number = request->call.number;
1346 ModemEvent * event;
1347 const char cmd[] = "ATD";
1348 const char anonymous[] = "I";
1349 const char voice[] = ";";
1350 size_t len;
1351
1352 if(request->call.number == NULL)
1353 request->call.number = "";
1354 if(request->call.number[0] == '\0')
1355 number = "L";
1356 else if(!hayescommon_number_is_valid(request->call.number))
1357 return NULL;
1358 event = &channel->events[MODEM_EVENT_TYPE_CALL];
1359 /* XXX should really be set at the time of the call */
1360 event->call.call_type = request->call.call_type;
1361 free(channel->call_number);
1362 if(request->call.call_type == MODEM_CALL_TYPE_DATA)
1363 channel->call_number = NULL;
1364 else if((channel->call_number = strdup(request->call.number)) == NULL)
1365 return NULL;
1366 event->call.number = channel->call_number;
1367 len = sizeof(cmd) + strlen(number) + sizeof(anonymous) + sizeof(voice);
1368 if((ret = malloc(len)) == NULL)
1369 return NULL;
1370 snprintf(ret, len, "%s%s%s%s", cmd, number,
1371 (request->call.anonymous) ? anonymous : "",
1372 (request->call.call_type == MODEM_CALL_TYPE_VOICE)
1373 ? voice : "");
1374 return ret;
1375 }
1376
_request_attention_call_ussd(ModemRequest * request)1377 static char * _request_attention_call_ussd(ModemRequest * request)
1378 {
1379 char * ret;
1380 char const * number = request->call.number;
1381 const char cmd[] = "AT+CUSD=1,";
1382 size_t len;
1383
1384 if(request->call.number == NULL || request->call.number[0] == '\0')
1385 return NULL;
1386 len = sizeof(cmd) + strlen(number) + 2;
1387 if((ret = malloc(len)) == NULL)
1388 return NULL;
1389 /* XXX may also require setting dcs */
1390 snprintf(ret, len, "%s\"%s\"", cmd, number);
1391 return ret;
1392 }
1393
_request_attention_call_hangup(Hayes * hayes,HayesChannel * channel)1394 static char * _request_attention_call_hangup(Hayes * hayes,
1395 HayesChannel * channel)
1396 {
1397 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CONNECTION];
1398
1399 /* FIXME check that this works on all phones, including:
1400 * - while calling:
1401 * . still ringing => simply inject "\r\n"?
1402 * . in the queue => simply remove?
1403 * - while ringing (incoming) */
1404 if(channel->mode == HAYESCHANNEL_MODE_DATA)
1405 {
1406 event->connection.connected = 0;
1407 event->connection.in = 0;
1408 event->connection.out = 0;
1409 hayes->helper->event(hayes->helper->modem, event);
1410 _hayes_set_mode(hayes, channel, HAYESCHANNEL_MODE_INIT);
1411 return NULL;
1412 }
1413 /* return "ATH" if currently ringing */
1414 event = &channel->events[MODEM_EVENT_TYPE_CALL];
1415 if(event->call.direction == MODEM_CALL_DIRECTION_INCOMING
1416 && event->call.status == MODEM_CALL_STATUS_RINGING)
1417 return strdup("ATH");
1418 /* force all calls to terminate */
1419 return strdup("AT+CHUP");
1420 }
1421
_request_attention_connectivity(Hayes * hayes,HayesChannel * channel,unsigned int enabled)1422 static char * _request_attention_connectivity(Hayes * hayes,
1423 HayesChannel * channel, unsigned int enabled)
1424 {
1425 _hayes_request_type(hayes, channel, enabled
1426 ? HAYES_REQUEST_FUNCTIONAL_ENABLE
1427 : HAYES_REQUEST_FUNCTIONAL_DISABLE);
1428 return NULL;
1429 }
1430
_request_attention_contact_delete(HayesChannel * channel,unsigned int id)1431 static char * _request_attention_contact_delete(HayesChannel * channel,
1432 unsigned int id)
1433 {
1434 char const cmd[] = "AT+CPBW=";
1435 char buf[32];
1436
1437 /* FIXME store in the command itself */
1438 channel->events[MODEM_EVENT_TYPE_CONTACT_DELETED].contact_deleted.id
1439 = id;
1440 snprintf(buf, sizeof(buf), "%s%u%s", cmd, id, ",");
1441 return strdup(buf);
1442 }
1443
_request_attention_contact_edit(unsigned int id,char const * name,char const * number)1444 static char * _request_attention_contact_edit(unsigned int id,
1445 char const * name, char const * number)
1446 {
1447 char const cmd[] = "AT+CPBW=";
1448 char buf[128];
1449 char * p;
1450
1451 if(!hayescommon_number_is_valid(number)
1452 || name == NULL || strlen(name) == 0)
1453 /* XXX report error */
1454 return NULL;
1455 if((p = g_convert(name, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL))
1456 != NULL)
1457 {
1458 _hayes_convert_iso_string_to_gsm(p);
1459 name = p;
1460 }
1461 if(snprintf(buf, sizeof(buf), "%s%u%s\"%s\"%s%u%s\"%s\"", cmd, id, ",",
1462 (number[0] == '+') ? &number[1] : number, ",",
1463 (number[0] == '+') ? 145 : 129, ",", name)
1464 >= (int)sizeof(buf))
1465 {
1466 g_free(p);
1467 /* XXX report error */
1468 return NULL;
1469 }
1470 g_free(p);
1471 return strdup(buf);
1472 }
1473
_request_attention_contact_list(ModemRequest * request)1474 static char * _request_attention_contact_list(ModemRequest * request)
1475 {
1476 HayesRequestContactList * list = request->plugin.data;
1477 const char cmd[] = "AT+CPBR=";
1478 char buf[32];
1479
1480 if(list->to < list->from)
1481 list->to = list->from;
1482 snprintf(buf, sizeof(buf), "%s%u,%u", cmd, list->from, list->to);
1483 return strdup(buf);
1484 }
1485
_request_attention_contact_new(char const * name,char const * number)1486 static char * _request_attention_contact_new(char const * name,
1487 char const * number)
1488 {
1489 char const cmd[] = "AT+CPBW=";
1490 char buf[128];
1491 char * p;
1492
1493 if(!hayescommon_number_is_valid(number)
1494 || name == NULL || strlen(name) == 0)
1495 /* XXX report error */
1496 return NULL;
1497 if((p = g_convert(name, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL))
1498 != NULL)
1499 {
1500 _hayes_convert_iso_string_to_gsm(p);
1501 name = p;
1502 }
1503 if(snprintf(buf, sizeof(buf), "%s%s\"%s\"%s%u%s\"%s\"", cmd, ",",
1504 (number[0] == '+') ? &number[1] : number, ",",
1505 (number[0] == '+') ? 145 : 129, ",", name)
1506 >= (int)sizeof(buf))
1507 {
1508 g_free(p);
1509 /* XXX report error */
1510 return NULL;
1511 }
1512 g_free(p);
1513 return strdup(buf);
1514 }
1515
_request_attention_dtmf_send(ModemRequest * request)1516 static char * _request_attention_dtmf_send(ModemRequest * request)
1517 {
1518 const char cmd[] = "AT+VTS=";
1519 char buf[32];
1520 unsigned int dtmf = request->dtmf_send.dtmf;
1521
1522 if((dtmf < '0' || dtmf > '9') && (dtmf < 'A' || dtmf > 'D')
1523 && dtmf != '*' && dtmf != '#')
1524 return NULL;
1525 snprintf(buf, sizeof(buf), "%s%c", cmd, dtmf);
1526 return strdup(buf);
1527 }
1528
_request_attention_gprs(Hayes * hayes,HayesChannel * channel,char const * username,char const * password)1529 static char * _request_attention_gprs(Hayes * hayes, HayesChannel * channel,
1530 char const * username, char const * password)
1531 {
1532 free(channel->gprs_username);
1533 channel->gprs_username = (username != NULL) ? strdup(username) : NULL;
1534 free(channel->gprs_password);
1535 channel->gprs_password = (password != NULL) ? strdup(password) : NULL;
1536 /* check for errors */
1537 if((username != NULL && channel->gprs_username == NULL)
1538 || (password != NULL && channel->gprs_password == NULL))
1539 hayes->helper->error(NULL, strerror(errno), 1);
1540 return NULL; /* we don't need to issue any command */
1541 }
1542
_request_attention_message(unsigned int id)1543 static char * _request_attention_message(unsigned int id)
1544 {
1545 char const cmd[] = "AT+CMGR=";
1546 char buf[32];
1547
1548 /* FIXME force the message format to be in PDU mode? */
1549 snprintf(buf, sizeof(buf), "%s%u", cmd, id);
1550 return strdup(buf);
1551 }
1552
_request_attention_message_delete(HayesChannel * channel,unsigned int id)1553 static char * _request_attention_message_delete(HayesChannel * channel,
1554 unsigned int id)
1555 {
1556 char const cmd[] = "AT+CMGD=";
1557 char buf[32];
1558
1559 /* FIXME store in the command itself */
1560 channel->events[MODEM_EVENT_TYPE_MESSAGE_DELETED].message_deleted.id
1561 = id;
1562 snprintf(buf, sizeof(buf), "%s%u", cmd, id);
1563 return strdup(buf);
1564 }
1565
_request_attention_message_list(Hayes * hayes,HayesChannel * channel)1566 static char * _request_attention_message_list(Hayes * hayes,
1567 HayesChannel * channel)
1568 {
1569 ModemRequest request;
1570 HayesRequestMessageData * data;
1571
1572 memset(&request, 0, sizeof(request));
1573 /* request received unread messages */
1574 request.type = HAYES_REQUEST_MESSAGE_LIST_INBOX_UNREAD;
1575 if((data = malloc(sizeof(*data))) != NULL)
1576 {
1577 data->id = 0;
1578 data->folder = MODEM_MESSAGE_FOLDER_INBOX;
1579 data->status = MODEM_MESSAGE_STATUS_UNREAD;
1580 }
1581 if(_hayes_request_channel(hayes, channel, &request, data) != 0)
1582 free(data);
1583 /* request received read messages */
1584 request.type = HAYES_REQUEST_MESSAGE_LIST_INBOX_READ;
1585 if((data = malloc(sizeof(*data))) != NULL)
1586 {
1587 data->id = 0;
1588 data->folder = MODEM_MESSAGE_FOLDER_INBOX;
1589 data->status = MODEM_MESSAGE_STATUS_READ;
1590 }
1591 if(_hayes_request_channel(hayes, channel, &request, data) != 0)
1592 free(data);
1593 /* request sent unread messages */
1594 request.type = HAYES_REQUEST_MESSAGE_LIST_SENT_UNREAD;
1595 if((data = malloc(sizeof(*data))) != NULL)
1596 {
1597 data->id = 0;
1598 data->folder = MODEM_MESSAGE_FOLDER_OUTBOX;
1599 data->status = MODEM_MESSAGE_STATUS_UNREAD;
1600 }
1601 if(_hayes_request_channel(hayes, channel, &request, data) != 0)
1602 free(data);
1603 /* request sent read messages */
1604 request.type = HAYES_REQUEST_MESSAGE_LIST_SENT_READ;
1605 if((data = malloc(sizeof(*data))) != NULL)
1606 {
1607 data->id = 0;
1608 data->folder = MODEM_MESSAGE_FOLDER_OUTBOX;
1609 data->status = MODEM_MESSAGE_STATUS_READ;
1610 }
1611 if(_hayes_request_channel(hayes, channel, &request, data) != 0)
1612 free(data);
1613 return NULL;
1614 }
1615
_request_attention_message_send(Hayes * hayes,HayesChannel * channel,char const * number,ModemMessageEncoding encoding,size_t length,char const * content,void ** data)1616 static char * _request_attention_message_send(Hayes * hayes,
1617 HayesChannel * channel, char const * number,
1618 ModemMessageEncoding encoding, size_t length,
1619 char const * content, void ** data)
1620 {
1621 char * ret;
1622 char const cmd[] = "AT+CMGS=";
1623 char * pdu;
1624 size_t pdulen;
1625 size_t len;
1626
1627 if(_hayes_request_type(hayes, channel, HAYES_REQUEST_MESSAGE_FORMAT_PDU)
1628 != 0)
1629 return NULL;
1630 if((pdu = _hayes_message_to_pdu(channel, number, encoding, length,
1631 content)) == NULL)
1632 return NULL;
1633 len = sizeof(cmd) + 10;
1634 if((ret = malloc(len)) == NULL)
1635 {
1636 free(pdu);
1637 return NULL;
1638 }
1639 pdulen = strlen(pdu);
1640 if(hayeschannel_has_quirks(channel, HAYES_QUIRK_WANT_SMSC_IN_PDU))
1641 pdulen -= 2;
1642 snprintf(ret, len, "%s%lu", cmd, (unsigned long)pdulen / 2);
1643 *data = pdu;
1644 return ret;
1645 }
1646
_request_attention_password_set(Hayes * hayes,char const * name,char const * oldpassword,char const * newpassword)1647 static char * _request_attention_password_set(Hayes * hayes, char const * name,
1648 char const * oldpassword, char const * newpassword)
1649 {
1650 char * ret;
1651 size_t len;
1652 char const cpwd[] = "AT+CPWD=";
1653 char const * n;
1654
1655 if(name == NULL || oldpassword == NULL || newpassword == NULL)
1656 return NULL;
1657 if(strcmp(name, "SIM PIN") == 0)
1658 n = "SC";
1659 else
1660 return NULL;
1661 len = sizeof(cpwd) + strlen(n) + 2 + strlen(oldpassword) + 3
1662 + strlen(newpassword) + 3;
1663 if((ret = malloc(len)) == NULL)
1664 {
1665 hayes->helper->error(NULL, strerror(errno), 1);
1666 return NULL;
1667 }
1668 snprintf(ret, len, "%s\"%s\",\"%s\",\"%s\"", cpwd, n, oldpassword,
1669 newpassword);
1670 return ret;
1671 }
1672
_request_attention_registration(Hayes * hayes,HayesChannel * channel,ModemRegistrationMode mode,char const * _operator)1673 static char * _request_attention_registration(Hayes * hayes,
1674 HayesChannel * channel, ModemRegistrationMode mode,
1675 char const * _operator)
1676 {
1677 char const cops[] = "AT+COPS=";
1678 size_t len = sizeof(cops) + 5;
1679 char * p;
1680
1681 switch(mode)
1682 {
1683 case MODEM_REGISTRATION_MODE_AUTOMATIC:
1684 _hayes_request_type(hayes, channel,
1685 HAYES_REQUEST_REGISTRATION_AUTOMATIC);
1686 break;
1687 case MODEM_REGISTRATION_MODE_DISABLED:
1688 _hayes_request_type(hayes, channel,
1689 HAYES_REQUEST_REGISTRATION_DISABLED);
1690 break;
1691 case MODEM_REGISTRATION_MODE_MANUAL:
1692 if(_operator == NULL)
1693 return NULL;
1694 len += strlen(_operator);
1695 if((p = malloc(len)) == NULL)
1696 return NULL;
1697 snprintf(p, len, "%s=1,0,%s", cops, _operator);
1698 return p;
1699 case MODEM_REGISTRATION_MODE_UNKNOWN:
1700 break;
1701 }
1702 return NULL;
1703 }
1704
_request_attention_sim_pin(Hayes * hayes,HayesChannel * channel,char const * password)1705 static char * _request_attention_sim_pin(Hayes * hayes, HayesChannel * channel,
1706 char const * password)
1707 {
1708 char * ret;
1709 const char cmd[] = "AT+CPIN=";
1710 size_t len;
1711 char const * format;
1712
1713 if(password == NULL)
1714 return NULL;
1715 len = sizeof(cmd) + strlen(password) + 2;
1716 if((ret = malloc(len)) == NULL)
1717 {
1718 hayes->helper->error(NULL, strerror(errno), 1);
1719 return NULL;
1720 }
1721 format = hayeschannel_has_quirks(channel, HAYES_QUIRK_CPIN_NO_QUOTES)
1722 ? "%s%s" : "%s\"%s\"";
1723 snprintf(ret, len, format, cmd, password);
1724 return ret;
1725 }
1726
_request_attention_sim_puk(Hayes * hayes,HayesChannel * channel,char const * password)1727 static char * _request_attention_sim_puk(Hayes * hayes, HayesChannel * channel,
1728 char const * password)
1729 {
1730 char * ret;
1731 const char cmd[] = "AT+CPIN=";
1732 size_t len;
1733 char const * format;
1734
1735 if(password == NULL)
1736 return NULL;
1737 len = sizeof(cmd) + strlen(password) + 3;
1738 if((ret = malloc(len)) == NULL)
1739 {
1740 hayes->helper->error(NULL, strerror(errno), 1);
1741 return NULL;
1742 }
1743 format = hayeschannel_has_quirks(channel, HAYES_QUIRK_CPIN_NO_QUOTES)
1744 ? "%s%s," : "%s\"%s\",";
1745 snprintf(ret, len, format, cmd, password);
1746 return ret;
1747 }
1748
_request_attention_unsupported(Hayes * hayes,ModemRequest * request)1749 static char * _request_attention_unsupported(Hayes * hayes,
1750 ModemRequest * request)
1751 {
1752 HayesRequest * hrequest = request->unsupported.request;
1753 (void) hayes;
1754
1755 if(strcmp(request->unsupported.modem, plugin.name) != 0)
1756 return NULL;
1757 if(request->unsupported.size != sizeof(*hrequest))
1758 return NULL;
1759 switch(request->unsupported.request_type)
1760 {
1761 case HAYES_REQUEST_COMMAND_QUEUE:
1762 return strdup(hrequest->command_queue.command);
1763 default:
1764 return NULL;
1765 }
1766 }
1767
1768
1769 /* hayes_request_type */
_hayes_request_type(Hayes * hayes,HayesChannel * channel,ModemRequestType type)1770 static int _hayes_request_type(Hayes * hayes, HayesChannel * channel,
1771 ModemRequestType type)
1772 {
1773 ModemRequest request;
1774
1775 memset(&request, 0, sizeof(request));
1776 request.type = type;
1777 return _hayes_request_channel(hayes, channel, &request, NULL);
1778 }
1779
1780
1781 /* hayes_reset */
_hayes_reset(Hayes * hayes)1782 static void _hayes_reset(Hayes * hayes)
1783 {
1784 _hayes_stop(hayes);
1785 _hayes_start(hayes, hayes->retry);
1786 }
1787
1788
1789 /* callbacks */
1790 /* on_channel_authenticate */
_on_channel_authenticate(gpointer data)1791 static gboolean _on_channel_authenticate(gpointer data)
1792 {
1793 const guint timeout = 1000;
1794 HayesChannel * channel = data;
1795 Hayes * hayes = channel->hayes;
1796 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_AUTHENTICATION];
1797
1798 if(channel->authenticate_count++ < 10)
1799 {
1800 channel->authenticate_source = g_timeout_add(timeout,
1801 _on_channel_authenticate, channel);
1802 /* FIXME this must stop the "checking for SIM PIN" dialog */
1803 _hayes_trigger(hayes, MODEM_EVENT_TYPE_AUTHENTICATION);
1804 }
1805 else
1806 {
1807 channel->authenticate_count = 0;
1808 channel->authenticate_source = 0;
1809 event->authentication.status
1810 = MODEM_AUTHENTICATION_STATUS_ERROR;
1811 hayes->helper->event(hayes->helper->modem, event);
1812 }
1813 return FALSE;
1814 }
1815
1816
1817 /* on_channel_reset */
1818 static int _reset_open(Hayes * hayes);
1819 static int _reset_configure(Hayes * hayes, char const * device, int fd);
1820 static unsigned int _reset_configure_baudrate(Hayes * hayes,
1821 unsigned int baudrate);
1822
_on_channel_reset(gpointer data)1823 static gboolean _on_channel_reset(gpointer data)
1824 {
1825 HayesChannel * channel = data;
1826 Hayes * hayes = channel->hayes;
1827 ModemPluginHelper * helper = hayes->helper;
1828 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_STATUS];
1829 GError * error = NULL;
1830 int fd;
1831 char const * logfile;
1832
1833 #ifdef DEBUG
1834 fprintf(stderr, "DEBUG: %s()\n", __func__);
1835 #endif
1836 _hayes_stop(hayes);
1837 if((fd = _reset_open(hayes)) < 0)
1838 {
1839 if(event->status.status != MODEM_STATUS_UNAVAILABLE)
1840 {
1841 event->status.status = MODEM_STATUS_UNAVAILABLE;
1842 hayes->helper->event(hayes->helper->modem, event);
1843 }
1844 hayes->helper->error(NULL, error_get(NULL), 1);
1845 if(hayes->retry > 0)
1846 channel->source = g_timeout_add(hayes->retry,
1847 _on_channel_reset, channel);
1848 return FALSE;
1849 }
1850 event->status.status = MODEM_STATUS_UNKNOWN;
1851 /* logging */
1852 logfile = helper->config_get(helper->modem, "logfile");
1853 if(logfile != NULL)
1854 {
1855 if((channel->fp = fopen(logfile, "w")) == NULL)
1856 hayes->helper->error(NULL, strerror(errno), 1);
1857 else
1858 setvbuf(channel->fp, NULL, _IONBF, BUFSIZ);
1859 }
1860 channel->channel = g_io_channel_unix_new(fd);
1861 if(g_io_channel_set_encoding(channel->channel, NULL, &error)
1862 != G_IO_STATUS_NORMAL)
1863 {
1864 hayes->helper->error(hayes->helper->modem, error->message, 1);
1865 g_error_free(error);
1866 }
1867 g_io_channel_set_buffered(channel->channel, FALSE);
1868 channel->rd_source = g_io_add_watch(channel->channel, G_IO_IN,
1869 _on_watch_can_read, channel);
1870 channel->source = g_idle_add(_on_reset_settle, channel);
1871 return FALSE;
1872 }
1873
_reset_open(Hayes * hayes)1874 static int _reset_open(Hayes * hayes)
1875 {
1876 ModemPluginHelper * helper = hayes->helper;
1877 char const * device;
1878 int fd;
1879
1880 if((device = helper->config_get(helper->modem, "device")) == NULL)
1881 device = "/dev/cuaU0";
1882 if((fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
1883 return -error_set_code(1, "%s: %s", device, strerror(errno));
1884 if(_reset_configure(hayes, device, fd) != 0)
1885 {
1886 close(fd);
1887 return -1;
1888 }
1889 return fd;
1890 }
1891
_reset_configure(Hayes * hayes,char const * device,int fd)1892 static int _reset_configure(Hayes * hayes, char const * device, int fd)
1893 {
1894 ModemPluginHelper * helper = hayes->helper;
1895 unsigned int baudrate;
1896 unsigned int hwflow;
1897 struct stat st;
1898 int fl;
1899 struct termios term;
1900 char const * p;
1901
1902 /* baud rate */
1903 if((p = helper->config_get(helper->modem, "baudrate")) == NULL
1904 || (baudrate = strtoul(p, NULL, 10)) == 0)
1905 baudrate = 115200;
1906 baudrate = _reset_configure_baudrate(hayes, baudrate);
1907 /* hardware flow */
1908 if((p = helper->config_get(helper->modem, "hwflow")) == NULL
1909 || (hwflow = strtoul(p, NULL, 10)) != 0)
1910 hwflow = 1;
1911 if(flock(fd, LOCK_EX | LOCK_NB) != 0)
1912 return 1;
1913 fl = fcntl(fd, F_GETFL, 0);
1914 if(fcntl(fd, F_SETFL, fl & ~O_NONBLOCK) == -1)
1915 return 1;
1916 if(fstat(fd, &st) != 0)
1917 return 1;
1918 if(st.st_mode & S_IFCHR) /* character special */
1919 {
1920 if(tcgetattr(fd, &term) != 0)
1921 return 1;
1922 term.c_cflag &= ~(CSIZE | PARENB);
1923 term.c_cflag |= CS8;
1924 term.c_cflag |= CREAD;
1925 term.c_cflag |= CLOCAL;
1926 if(hwflow != 0)
1927 term.c_cflag |= CRTSCTS;
1928 else
1929 term.c_cflag &= ~CRTSCTS;
1930 term.c_iflag = (IGNPAR | IGNBRK);
1931 term.c_lflag = 0;
1932 term.c_oflag = 0;
1933 term.c_cc[VMIN] = 1;
1934 term.c_cc[VTIME] = 0;
1935 if(cfsetispeed(&term, 0) != 0) /* same speed as output speed */
1936 error_set("%s", device); /* go on anyway */
1937 if(cfsetospeed(&term, baudrate) != 0)
1938 error_set("%s", device); /* go on anyway */
1939 if(tcsetattr(fd, TCSAFLUSH, &term) != 0)
1940 return 1;
1941 }
1942 return 0;
1943 }
1944
_reset_configure_baudrate(Hayes * hayes,unsigned int baudrate)1945 static unsigned int _reset_configure_baudrate(Hayes * hayes,
1946 unsigned int baudrate)
1947 {
1948 switch(baudrate)
1949 {
1950 case 1200:
1951 return B1200;
1952 case 2400:
1953 return B2400;
1954 case 4800:
1955 return B4800;
1956 case 9600:
1957 return B9600;
1958 case 19200:
1959 return B19200;
1960 case 38400:
1961 return B38400;
1962 #ifdef B76800
1963 case 76800:
1964 return B76800;
1965 #endif
1966 #ifdef B14400
1967 case 14400:
1968 return B14400;
1969 #endif
1970 #ifdef B28800
1971 case 28800:
1972 return B28800;
1973 #endif
1974 case 57600:
1975 return B57600;
1976 case 115200:
1977 return B115200;
1978 case 230400:
1979 return B230400;
1980 #ifdef B460800
1981 case 460800:
1982 return B460800;
1983 #endif
1984 #ifdef B921600
1985 case 921600:
1986 return B921600;
1987 #endif
1988 default:
1989 error_set("%u%s%u%s", baudrate,
1990 ": Unsupported baudrate (using ",
1991 115200, ")");
1992 hayes->helper->error(NULL, error_get(NULL), 1);
1993 return B115200;
1994 }
1995 }
1996
1997
1998 /* on_channel_timeout */
_on_channel_timeout(gpointer data)1999 static gboolean _on_channel_timeout(gpointer data)
2000 {
2001 HayesChannel * channel = data;
2002 Hayes * hayes = channel->hayes;
2003 HayesCommand * command;
2004
2005 #ifdef DEBUG
2006 fprintf(stderr, "DEBUG: %s()\n", __func__);
2007 #endif
2008 channel->timeout = 0;
2009 if(channel->queue == NULL || (command = channel->queue->data) == NULL)
2010 return FALSE;
2011 hayes_command_set_status(command, HCS_TIMEOUT);
2012 hayeschannel_queue_pop(channel);
2013 _hayes_queue_push(hayes, channel);
2014 return FALSE;
2015 }
2016
2017
2018 /* on_queue_timeout */
_on_queue_timeout(gpointer data)2019 static gboolean _on_queue_timeout(gpointer data)
2020 {
2021 const guint timeout = 1000;
2022 HayesChannel * channel = data;
2023 Hayes * hayes = channel->hayes;
2024 HayesCommand * command;
2025
2026 channel->source = 0;
2027 if(channel->queue_timeout == NULL) /* nothing to send */
2028 return FALSE;
2029 command = channel->queue_timeout->data;
2030 _hayes_queue_command(hayes, channel, command);
2031 channel->queue_timeout = g_slist_remove(channel->queue_timeout,
2032 command);
2033 if(channel->queue_timeout != NULL)
2034 channel->source = g_timeout_add(timeout, _on_queue_timeout,
2035 channel);
2036 else
2037 /* XXX check the registration again to be safe */
2038 _hayes_request_type(hayes, channel, HAYES_REQUEST_REGISTRATION);
2039 return FALSE;
2040 }
2041
2042
2043 /* on_reset_settle */
2044 static void _reset_settle_command(HayesChannel * channel, char const * string);
2045 static HayesCommandStatus _on_reset_settle_callback(HayesCommand * command,
2046 HayesCommandStatus status, HayesChannel * channel);
2047
_on_reset_settle(gpointer data)2048 static gboolean _on_reset_settle(gpointer data)
2049 {
2050 HayesChannel * channel = data;
2051
2052 channel->source = 0;
2053 _reset_settle_command(channel, "ATZE0V1");
2054 return FALSE;
2055 }
2056
_on_reset_settle2(gpointer data)2057 static gboolean _on_reset_settle2(gpointer data)
2058 {
2059 HayesChannel * channel = data;
2060
2061 channel->source = 0;
2062 /* try an alternative initialization string */
2063 _reset_settle_command(channel, "ATE0V1");
2064 return FALSE;
2065 }
2066
_reset_settle_command(HayesChannel * channel,char const * string)2067 static void _reset_settle_command(HayesChannel * channel, char const * string)
2068 {
2069 const unsigned int timeout = 500;
2070 Hayes * hayes = channel->hayes;
2071 HayesCommand * command;
2072
2073 #ifdef DEBUG
2074 fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, string);
2075 #endif
2076 if((command = hayes_command_new(string)) == NULL)
2077 {
2078 hayes->helper->error(hayes->helper->modem, error_get(NULL), 1);
2079 return;
2080 }
2081 hayes_command_set_callback(command, _on_reset_settle_callback, channel);
2082 hayes_command_set_priority(command, HCP_IMMEDIATE);
2083 hayes_command_set_timeout(command, timeout);
2084 if(_hayes_queue_command(hayes, channel, command) != 0)
2085 {
2086 hayes->helper->error(hayes->helper->modem, error_get(NULL), 1);
2087 hayes_command_delete(command);
2088 }
2089 }
2090
_on_reset_settle_callback(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2091 static HayesCommandStatus _on_reset_settle_callback(HayesCommand * command,
2092 HayesCommandStatus status, HayesChannel * channel)
2093 {
2094 Hayes * hayes = channel->hayes;
2095
2096 #ifdef DEBUG
2097 fprintf(stderr, "DEBUG: %s(%s (%u))\n", __func__,
2098 _hayes_command_status[status], status);
2099 #endif
2100 status = _on_request_generic(command, status, channel);
2101 switch(status)
2102 {
2103 case HCS_UNKNOWN: /* ignore */
2104 case HCS_QUEUED:
2105 case HCS_PENDING:
2106 break;
2107 case HCS_ACTIVE: /* give it another chance */
2108 break;
2109 case HCS_SUCCESS: /* we can initialize */
2110 _hayes_set_mode(hayes, channel,
2111 HAYESCHANNEL_MODE_COMMAND);
2112 _hayes_request_type(hayes, channel,
2113 HAYES_REQUEST_LOCAL_ECHO_DISABLE);
2114 _hayes_request_type(hayes, channel,
2115 HAYES_REQUEST_VERBOSE_ENABLE);
2116 _hayes_request_type(hayes, channel,
2117 HAYES_REQUEST_VENDOR);
2118 _hayes_request_type(hayes, channel,
2119 HAYES_REQUEST_MODEL);
2120 _hayes_request_type(hayes, channel,
2121 HAYES_REQUEST_EXTENDED_ERRORS);
2122 _hayes_request_type(hayes, channel,
2123 HAYES_REQUEST_FUNCTIONAL);
2124 break;
2125 case HCS_TIMEOUT: /* try again */
2126 case HCS_ERROR:
2127 if(channel->source != 0)
2128 g_source_remove(channel->source);
2129 channel->source = g_timeout_add(hayes->retry,
2130 _on_reset_settle2, channel);
2131 break;
2132 }
2133 return status;
2134 }
2135
2136
2137 /* on_watch_can_read */
_on_watch_can_read(GIOChannel * source,GIOCondition condition,gpointer data)2138 static gboolean _on_watch_can_read(GIOChannel * source, GIOCondition condition,
2139 gpointer data)
2140 {
2141 HayesChannel * channel = data;
2142 Hayes * hayes = channel->hayes;
2143 ModemPluginHelper * helper = hayes->helper;
2144 const int inc = 256;
2145 gsize cnt = 0;
2146 GError * error = NULL;
2147 GIOStatus status;
2148 char * p;
2149
2150 if(condition != G_IO_IN || source != channel->channel)
2151 return FALSE; /* should not happen */
2152 if((p = realloc(channel->rd_buf, channel->rd_buf_cnt + inc)) == NULL)
2153 return TRUE; /* XXX retries immediately (delay?) */
2154 channel->rd_buf = p;
2155 status = g_io_channel_read_chars(source,
2156 &channel->rd_buf[channel->rd_buf_cnt], inc, &cnt,
2157 &error);
2158 _hayes_log(hayes, channel, "MODEM: ",
2159 &channel->rd_buf[channel->rd_buf_cnt], cnt);
2160 channel->rd_buf_cnt += cnt;
2161 switch(status)
2162 {
2163 case G_IO_STATUS_NORMAL:
2164 break;
2165 case G_IO_STATUS_ERROR:
2166 helper->error(helper->modem, error->message, 1);
2167 g_error_free(error);
2168 /* fallback */
2169 case G_IO_STATUS_EOF:
2170 default: /* should not happen... */
2171 channel->rd_source = 0;
2172 if(hayes->retry > 0)
2173 _hayes_reset(hayes);
2174 return FALSE;
2175 }
2176 switch(channel->mode)
2177 {
2178 case HAYESCHANNEL_MODE_INIT:
2179 case HAYESCHANNEL_MODE_COMMAND:
2180 _hayes_parse(hayes, channel);
2181 break;
2182 case HAYESCHANNEL_MODE_PDU:
2183 _hayes_parse_pdu(hayes, channel);
2184 break;
2185 case HAYESCHANNEL_MODE_DATA:
2186 if(channel->wr_ppp_channel == NULL
2187 || channel->wr_ppp_source != 0)
2188 break;
2189 channel->wr_ppp_source = g_io_add_watch(
2190 channel->wr_ppp_channel, G_IO_OUT,
2191 _on_watch_can_write_ppp, channel);
2192 break;
2193 }
2194 return TRUE;
2195 }
2196
2197
2198 /* on_watch_can_read_ppp */
_on_watch_can_read_ppp(GIOChannel * source,GIOCondition condition,gpointer data)2199 static gboolean _on_watch_can_read_ppp(GIOChannel * source,
2200 GIOCondition condition, gpointer data)
2201 {
2202 HayesChannel * channel = data;
2203 Hayes * hayes = channel->hayes;
2204 ModemPluginHelper * helper = hayes->helper;
2205 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CONNECTION];
2206 const int inc = 256;
2207 gsize cnt = 0;
2208 GError * error = NULL;
2209 GIOStatus status;
2210 char * p;
2211
2212 if(condition != G_IO_IN || source != channel->rd_ppp_channel)
2213 return FALSE; /* should not happen */
2214 if((p = realloc(channel->wr_buf, channel->wr_buf_cnt + inc)) == NULL)
2215 return TRUE; /* XXX retries immediately (delay?) */
2216 channel->wr_buf = p;
2217 status = g_io_channel_read_chars(source,
2218 &channel->wr_buf[channel->wr_buf_cnt], inc, &cnt,
2219 &error);
2220 channel->wr_buf_cnt += cnt;
2221 event->connection.out += cnt;
2222 switch(status)
2223 {
2224 case G_IO_STATUS_NORMAL:
2225 break;
2226 case G_IO_STATUS_ERROR:
2227 helper->error(helper->modem, error->message, 1);
2228 g_error_free(error);
2229 /* fallback */
2230 case G_IO_STATUS_EOF:
2231 default:
2232 channel->rd_ppp_source = 0;
2233 event->connection.connected = 0;
2234 helper->event(helper->modem, event);
2235 _hayes_set_mode(hayes, channel, HAYESCHANNEL_MODE_INIT);
2236 return FALSE;
2237 }
2238 if(channel->channel != NULL && channel->wr_source == 0)
2239 channel->wr_source = g_io_add_watch(channel->channel, G_IO_OUT,
2240 _on_watch_can_write, channel);
2241 return TRUE;
2242 }
2243
2244
2245 /* on_watch_can_write */
_on_watch_can_write(GIOChannel * source,GIOCondition condition,gpointer data)2246 static gboolean _on_watch_can_write(GIOChannel * source, GIOCondition condition,
2247 gpointer data)
2248 {
2249 HayesChannel * channel = data;
2250 HayesCommand * command = (channel->queue) != NULL
2251 ? channel->queue->data : NULL;
2252 Hayes * hayes = channel->hayes;
2253 ModemPluginHelper * helper = hayes->helper;
2254 gsize cnt = 0;
2255 GError * error = NULL;
2256 GIOStatus status;
2257 char * p;
2258
2259 if(condition != G_IO_OUT || source != channel->channel)
2260 return FALSE; /* should not happen */
2261 status = g_io_channel_write_chars(source, channel->wr_buf,
2262 channel->wr_buf_cnt, &cnt, &error);
2263 _hayes_log(hayes, channel, "PHONE: ", channel->wr_buf, cnt);
2264 if(cnt != 0) /* some data may have been written anyway */
2265 {
2266 channel->wr_buf_cnt -= cnt;
2267 memmove(channel->wr_buf, &channel->wr_buf[cnt],
2268 channel->wr_buf_cnt);
2269 if((p = realloc(channel->wr_buf, channel->wr_buf_cnt)) != NULL)
2270 /* we can ignore errors... */
2271 channel->wr_buf = p;
2272 else if(channel->wr_buf_cnt == 0)
2273 /* ...except when it's not one */
2274 channel->wr_buf = NULL;
2275 }
2276 switch(status)
2277 {
2278 case G_IO_STATUS_NORMAL:
2279 break;
2280 case G_IO_STATUS_ERROR:
2281 helper->error(helper->modem, error->message, 1);
2282 g_error_free(error);
2283 /* fallback */
2284 case G_IO_STATUS_EOF:
2285 default: /* should not happen */
2286 channel->wr_source = 0;
2287 if(hayes->retry > 0)
2288 _hayes_reset(hayes);
2289 return FALSE;
2290 }
2291 if(channel->wr_buf_cnt > 0) /* there is more data to write */
2292 return TRUE;
2293 channel->wr_source = 0;
2294 if(command != NULL)
2295 hayes_command_set_status(command, HCS_ACTIVE);
2296 return FALSE;
2297 }
2298
2299
2300 /* on_watch_can_write_ppp */
_on_watch_can_write_ppp(GIOChannel * source,GIOCondition condition,gpointer data)2301 static gboolean _on_watch_can_write_ppp(GIOChannel * source,
2302 GIOCondition condition, gpointer data)
2303 {
2304 HayesChannel * channel = data;
2305 Hayes * hayes = channel->hayes;
2306 ModemPluginHelper * helper = hayes->helper;
2307 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CONNECTION];
2308 gsize cnt = 0;
2309 GError * error = NULL;
2310 GIOStatus status;
2311 char * p;
2312
2313 if(condition != G_IO_OUT || source != channel->wr_ppp_channel)
2314 return FALSE; /* should not happen */
2315 status = g_io_channel_write_chars(source, channel->rd_buf,
2316 channel->rd_buf_cnt, &cnt, &error);
2317 event->connection.in += cnt;
2318 if(cnt != 0) /* some data may have been written anyway */
2319 {
2320 channel->rd_buf_cnt -= cnt;
2321 memmove(channel->rd_buf, &channel->rd_buf[cnt],
2322 channel->rd_buf_cnt);
2323 if((p = realloc(channel->rd_buf, channel->rd_buf_cnt)) != NULL)
2324 /* we can ignore errors... */
2325 channel->rd_buf = p;
2326 else if(channel->rd_buf_cnt == 0)
2327 /* ...except when it's not one */
2328 channel->rd_buf = NULL;
2329 }
2330 switch(status)
2331 {
2332 case G_IO_STATUS_NORMAL:
2333 break;
2334 case G_IO_STATUS_ERROR:
2335 helper->error(helper->modem, error->message, 1);
2336 g_error_free(error);
2337 /* fallback */
2338 case G_IO_STATUS_EOF:
2339 default:
2340 channel->wr_ppp_source = 0;
2341 event->connection.connected = 0;
2342 helper->event(helper->modem, event);
2343 _hayes_set_mode(hayes, channel, HAYESCHANNEL_MODE_INIT);
2344 return FALSE;
2345 }
2346 if(channel->rd_buf_cnt > 0) /* there is more data to write */
2347 return TRUE;
2348 channel->wr_ppp_source = 0;
2349 return FALSE;
2350 }
2351
2352
2353 /* on_request_authenticate */
_on_request_authenticate(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2354 static HayesCommandStatus _on_request_authenticate(HayesCommand * command,
2355 HayesCommandStatus status, HayesChannel * channel)
2356 {
2357 const char sim_pin[] = "SIM PIN";
2358 const char sim_puk[] = "SIM PUK";
2359 Hayes * hayes = channel->hayes;
2360 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_AUTHENTICATION];
2361 guint timeout;
2362
2363 switch((status = _on_request_generic(command, status, channel)))
2364 {
2365 case HCS_SUCCESS:
2366 break;
2367 case HCS_ERROR:
2368 event->authentication.status
2369 = MODEM_AUTHENTICATION_STATUS_ERROR;
2370 hayes->helper->event(hayes->helper->modem, event);
2371 /* fallback */
2372 default:
2373 return status;
2374 }
2375 /* XXX it should be bound to the request instead */
2376 if(event->authentication.name != NULL && (strcmp(sim_pin,
2377 event->authentication.name) == 0
2378 || strcmp(sim_puk,
2379 event->authentication.name) == 0))
2380 {
2381 /* verify that it really worked */
2382 timeout = hayeschannel_has_quirks(channel,
2383 HAYES_QUIRK_CPIN_SLOW) ? 1000 : 0;
2384 channel->authenticate_count = 0;
2385 if(channel->authenticate_source != 0)
2386 g_source_remove(channel->authenticate_source);
2387 channel->authenticate_source = g_timeout_add(timeout,
2388 _on_channel_authenticate, channel);
2389 }
2390 else
2391 {
2392 event->authentication.status = MODEM_AUTHENTICATION_STATUS_OK;
2393 hayes->helper->event(hayes->helper->modem, event);
2394 }
2395 return status;
2396 }
2397
2398
2399 /* on_request_battery_level */
_on_request_battery_level(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2400 static HayesCommandStatus _on_request_battery_level(HayesCommand * command,
2401 HayesCommandStatus status, HayesChannel * channel)
2402 {
2403 Hayes * hayes = channel->hayes;
2404 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_BATTERY_LEVEL];
2405
2406 if((status = _on_request_generic(command, status, channel))
2407 != HCS_SUCCESS)
2408 return status;
2409 hayes->helper->event(hayes->helper->modem, event);
2410 return status;
2411 }
2412
2413
2414 /* on_request_call */
_on_request_call(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2415 static HayesCommandStatus _on_request_call(HayesCommand * command,
2416 HayesCommandStatus status, HayesChannel * channel)
2417 {
2418 Hayes * hayes = channel->hayes;
2419 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CALL];
2420
2421 if((status = _on_request_generic(command, status, channel))
2422 != HCS_SUCCESS)
2423 return status;
2424 hayes->helper->event(hayes->helper->modem, event);
2425 return status;
2426 }
2427
2428
2429 /* on_request_call_incoming */
_on_request_call_incoming(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2430 static HayesCommandStatus _on_request_call_incoming(HayesCommand * command,
2431 HayesCommandStatus status, HayesChannel * channel)
2432 {
2433 Hayes * hayes = channel->hayes;
2434 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CALL];
2435
2436 if((status = _on_request_generic(command, status, channel))
2437 != HCS_SUCCESS && status != HCS_ERROR)
2438 return status;
2439 event->call.direction = MODEM_CALL_DIRECTION_INCOMING;
2440 event->call.status = (status == HCS_SUCCESS)
2441 ? MODEM_CALL_STATUS_ACTIVE : MODEM_CALL_STATUS_NONE;
2442 hayes->helper->event(hayes->helper->modem, event);
2443 return status;
2444 }
2445
2446
2447 /* on_request_call_outgoing */
_on_request_call_outgoing(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2448 static HayesCommandStatus _on_request_call_outgoing(HayesCommand * command,
2449 HayesCommandStatus status, HayesChannel * channel)
2450 {
2451 Hayes * hayes = channel->hayes;
2452 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CALL];
2453
2454 if((status = _on_request_generic(command, status, channel))
2455 != HCS_SUCCESS && status != HCS_ERROR)
2456 return status;
2457 event->call.direction = MODEM_CALL_DIRECTION_OUTGOING;
2458 event->call.status = (status == HCS_SUCCESS)
2459 ? MODEM_CALL_STATUS_ACTIVE : MODEM_CALL_STATUS_NONE;
2460 hayes->helper->event(hayes->helper->modem, event);
2461 return status;
2462 }
2463
2464
2465 /* on_request_call_status */
_on_request_call_status(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2466 static HayesCommandStatus _on_request_call_status(HayesCommand * command,
2467 HayesCommandStatus status, HayesChannel * channel)
2468 {
2469 Hayes * hayes = channel->hayes;
2470
2471 if((status = _on_request_generic(command, status, channel))
2472 != HCS_SUCCESS && status != HCS_ERROR)
2473 return status;
2474 _hayes_request_type(hayes, channel, HAYES_REQUEST_PHONE_ACTIVE);
2475 return status;
2476 }
2477
2478
2479 /* on_request_contact_delete */
_on_request_contact_delete(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2480 static HayesCommandStatus _on_request_contact_delete(HayesCommand * command,
2481 HayesCommandStatus status, HayesChannel * channel)
2482 {
2483 Hayes * hayes = channel->hayes;
2484 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CONTACT_DELETED];
2485
2486 if((status = _on_request_generic(command, status, channel))
2487 != HCS_SUCCESS)
2488 return status;
2489 hayes->helper->event(hayes->helper->modem, event);
2490 return status;
2491 }
2492
2493
2494 /* on_request_contact_list */
_on_request_contact_list(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2495 static HayesCommandStatus _on_request_contact_list(HayesCommand * command,
2496 HayesCommandStatus status, HayesChannel * channel)
2497 {
2498 if((status = _on_request_generic(command, status, channel))
2499 != HCS_SUCCESS)
2500 return status;
2501 /* XXX could probably be more efficient */
2502 _hayes_request_type(channel->hayes, channel,
2503 MODEM_REQUEST_CONTACT_LIST);
2504 return status;
2505 }
2506
2507
2508 /* on_request_functional */
_on_request_functional(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2509 static HayesCommandStatus _on_request_functional(HayesCommand * command,
2510 HayesCommandStatus status, HayesChannel * channel)
2511 {
2512 Hayes * hayes = channel->hayes;
2513
2514 switch((status = _on_request_generic(command, status, channel)))
2515 {
2516 case HCS_ERROR:
2517 /* try to enable */
2518 _hayes_request_type(hayes, channel,
2519 HAYES_REQUEST_FUNCTIONAL_ENABLE);
2520 break;
2521 default:
2522 break;
2523 }
2524 return status;
2525 }
2526
2527
2528 /* on_request_functional_enable */
_on_request_functional_enable(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2529 static HayesCommandStatus _on_request_functional_enable(HayesCommand * command,
2530 HayesCommandStatus status, HayesChannel * channel)
2531 {
2532 Hayes * hayes = channel->hayes;
2533
2534 switch((status = _on_request_generic(command, status, channel)))
2535 {
2536 case HCS_ERROR:
2537 #if 0 /* XXX ignore for now (may simply be missing the PIN code) */
2538 /* force a reset */
2539 _hayes_request_type(hayes,
2540 HAYES_REQUEST_FUNCTIONAL_ENABLE_RESET);
2541 #endif
2542 break;
2543 case HCS_SUCCESS:
2544 _on_code_cfun(channel, "1"); /* XXX ugly workaround */
2545 break;
2546 case HCS_TIMEOUT:
2547 /* repeat request */
2548 _hayes_request_type(hayes, channel,
2549 HAYES_REQUEST_FUNCTIONAL_ENABLE);
2550 break;
2551 default:
2552 break;
2553 }
2554 return status;
2555 }
2556
2557
2558 /* on_request_functional_enable_reset */
_on_request_functional_enable_reset(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2559 static HayesCommandStatus _on_request_functional_enable_reset(
2560 HayesCommand * command, HayesCommandStatus status,
2561 HayesChannel * channel)
2562 {
2563 Hayes * hayes = channel->hayes;
2564
2565 switch((status = _on_request_generic(command, status, channel)))
2566 {
2567 case HCS_SUCCESS:
2568 _on_code_cfun(channel, "1"); /* XXX ugly workaround */
2569 break;
2570 case HCS_TIMEOUT:
2571 /* repeat request */
2572 _hayes_request_type(hayes, channel,
2573 HAYES_REQUEST_FUNCTIONAL_ENABLE);
2574 break;
2575 default:
2576 break;
2577 }
2578 return status;
2579 }
2580
2581
2582 /* on_request_generic */
_on_request_generic(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2583 static HayesCommandStatus _on_request_generic(HayesCommand * command,
2584 HayesCommandStatus status, HayesChannel * channel)
2585 {
2586 char const * answer;
2587 char const * p;
2588 (void) channel;
2589
2590 if(status != HCS_ACTIVE)
2591 return status;
2592 if((answer = hayes_command_get_answer(command)) == NULL)
2593 return status;
2594 /* look for the last line */
2595 while((p = strchr(answer, '\n')) != NULL)
2596 answer = ++p;
2597 if(strcmp(answer, "OK") == 0)
2598 return HCS_SUCCESS;
2599 else if(strcmp(answer, "ERROR") == 0)
2600 return HCS_ERROR;
2601 return status;
2602 }
2603
2604
2605 /* on_request_message */
_on_request_message(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2606 static HayesCommandStatus _on_request_message(HayesCommand * command,
2607 HayesCommandStatus status, HayesChannel * channel)
2608 {
2609 HayesRequestMessageData * data;
2610
2611 if((status = _on_request_generic(command, status, channel))
2612 == HCS_SUCCESS
2613 || status == HCS_ERROR || status == HCS_TIMEOUT)
2614 if((data = hayes_command_get_data(command)) != NULL)
2615 {
2616 free(data);
2617 hayes_command_set_data(command, NULL);
2618 }
2619 return status;
2620 }
2621
2622
2623 /* on_request_message_delete */
_on_request_message_delete(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2624 static HayesCommandStatus _on_request_message_delete(HayesCommand * command,
2625 HayesCommandStatus status, HayesChannel * channel)
2626 {
2627 Hayes * hayes = channel->hayes;
2628 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MESSAGE_DELETED];
2629
2630 if((status = _on_request_generic(command, status, channel))
2631 != HCS_SUCCESS)
2632 return status;
2633 hayes->helper->event(hayes->helper->modem, event);
2634 return status;
2635 }
2636
2637
2638 /* on_request_message_list */
_on_request_message_list(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2639 static HayesCommandStatus _on_request_message_list(HayesCommand * command,
2640 HayesCommandStatus status, HayesChannel * channel)
2641 {
2642 HayesRequestMessageData * data;
2643
2644 if((status = _on_request_generic(command, status, channel))
2645 == HCS_SUCCESS
2646 || status == HCS_ERROR || status == HCS_TIMEOUT)
2647 if((data = hayes_command_get_data(command)) != NULL)
2648 {
2649 free(data);
2650 hayes_command_set_data(command, NULL);
2651 }
2652 return status;
2653 }
2654
2655
2656 /* on_request_message_send */
_on_request_message_send(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2657 static HayesCommandStatus _on_request_message_send(HayesCommand * command,
2658 HayesCommandStatus status, HayesChannel * channel)
2659 {
2660 Hayes * hayes = channel->hayes;
2661 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MESSAGE_SENT];
2662 char * pdu;
2663
2664 if((pdu = hayes_command_get_data(command)) != NULL
2665 && (status = _on_request_generic(command, status,
2666 channel)) == HCS_ACTIVE)
2667 _hayes_set_mode(hayes, channel, HAYESCHANNEL_MODE_PDU);
2668 if(status == HCS_SUCCESS || status == HCS_ERROR
2669 || status == HCS_TIMEOUT)
2670 {
2671 free(pdu);
2672 hayes_command_set_data(command, NULL);
2673 }
2674 if(status == HCS_ERROR)
2675 {
2676 event->message_sent.error = "Could not send message";
2677 event->message_sent.id = 0;
2678 hayes->helper->event(hayes->helper->modem, event);
2679 }
2680 return status;
2681 }
2682
2683
2684 /* on_request_model */
_on_request_model(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2685 static HayesCommandStatus _on_request_model(HayesCommand * command,
2686 HayesCommandStatus status, HayesChannel * channel)
2687 {
2688 Hayes * hayes = channel->hayes;
2689 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MODEL];
2690
2691 if((status = _on_request_generic(command, status, channel))
2692 != HCS_SUCCESS)
2693 return status;
2694 hayes->helper->event(hayes->helper->modem, event);
2695 return status;
2696 }
2697
2698
2699 /* on_request_registration */
_on_request_registration(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2700 static HayesCommandStatus _on_request_registration(HayesCommand * command,
2701 HayesCommandStatus status, HayesChannel * channel)
2702 {
2703 Hayes * hayes = channel->hayes;
2704
2705 if((status = _on_request_generic(command, status, channel))
2706 != HCS_SUCCESS)
2707 return status;
2708 /* force a registration status */
2709 _hayes_request_type(hayes, channel, HAYES_REQUEST_REGISTRATION);
2710 return status;
2711 }
2712
2713
2714 /* on_request_registration_automatic */
_on_request_registration_automatic(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2715 static HayesCommandStatus _on_request_registration_automatic(
2716 HayesCommand * command, HayesCommandStatus status, HayesChannel * channel)
2717 {
2718 Hayes * hayes = channel->hayes;
2719 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
2720
2721 status = _on_request_generic(command, status, channel);
2722 switch(status)
2723 {
2724 case HCS_UNKNOWN:
2725 case HCS_QUEUED:
2726 case HCS_PENDING:
2727 break;
2728 case HCS_ACTIVE:
2729 event->registration.mode
2730 = MODEM_REGISTRATION_MODE_AUTOMATIC;
2731 event->registration.status
2732 = MODEM_REGISTRATION_STATUS_SEARCHING;
2733 hayes->helper->event(hayes->helper->modem, event);
2734 break;
2735 case HCS_ERROR:
2736 case HCS_TIMEOUT:
2737 event->registration.mode
2738 = MODEM_REGISTRATION_MODE_UNKNOWN;
2739 event->registration.status
2740 = MODEM_REGISTRATION_STATUS_UNKNOWN;
2741 hayes->helper->event(hayes->helper->modem, event);
2742 break;
2743 case HCS_SUCCESS:
2744 /* force a registration status */
2745 _hayes_request_type(hayes, channel,
2746 HAYES_REQUEST_REGISTRATION);
2747 break;
2748 }
2749 return status;
2750 }
2751
2752
2753 /* on_request_registration_disabled */
_on_request_registration_disabled(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2754 static HayesCommandStatus _on_request_registration_disabled(
2755 HayesCommand * command, HayesCommandStatus status, HayesChannel * channel)
2756 {
2757 Hayes * hayes = channel->hayes;
2758 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
2759
2760 if((status = _on_request_generic(command, status, channel))
2761 != HCS_SUCCESS)
2762 return status;
2763 event->registration.mode = MODEM_REGISTRATION_MODE_DISABLED;
2764 /* force a registration status */
2765 _hayes_request_type(hayes, channel, HAYES_REQUEST_REGISTRATION);
2766 return status;
2767 }
2768
2769
2770 /* on_request_sim_pin_valid */
_on_request_sim_pin_valid(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2771 static HayesCommandStatus _on_request_sim_pin_valid(HayesCommand * command,
2772 HayesCommandStatus status, HayesChannel * channel)
2773 {
2774 Hayes * hayes = channel->hayes;
2775 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_AUTHENTICATION];
2776 ModemRequest request;
2777
2778 #ifdef DEBUG
2779 fprintf(stderr, "DEBUG: %s(%u)\n", __func__, status);
2780 #endif
2781 if((status = _on_request_generic(command, status, channel)) == HCS_ERROR
2782 || status == HCS_TIMEOUT)
2783 {
2784 event->authentication.status
2785 = MODEM_AUTHENTICATION_STATUS_ERROR;
2786 hayes->helper->event(hayes->helper->modem, event);
2787 return status;
2788 }
2789 else if(status != HCS_SUCCESS)
2790 return status;
2791 hayes->helper->event(hayes->helper->modem, event);
2792 /* return if not successful */
2793 if(event->authentication.status != MODEM_AUTHENTICATION_STATUS_OK)
2794 return status;
2795 /* apply useful settings */
2796 _hayes_request_type(hayes, channel, HAYES_REQUEST_EXTENDED_ERRORS);
2797 _hayes_request_type(hayes, channel,
2798 HAYES_REQUEST_EXTENDED_RING_REPORTS);
2799 memset(&request, 0, sizeof(request));
2800 request.type = MODEM_REQUEST_CALL_PRESENTATION;
2801 request.call_presentation.enabled = 1;
2802 _hayes_request(hayes, &request);
2803 _hayes_request_type(hayes, channel,
2804 HAYES_REQUEST_CALL_WAITING_UNSOLLICITED_ENABLE);
2805 _hayes_request_type(hayes, channel,
2806 HAYES_REQUEST_CONNECTED_LINE_ENABLE);
2807 /* report new messages */
2808 _hayes_request_type(hayes, channel,
2809 HAYES_REQUEST_MESSAGE_UNSOLLICITED_ENABLE);
2810 /* report new notifications */
2811 _hayes_request_type(hayes, channel,
2812 HAYES_REQUEST_SUPPLEMENTARY_SERVICE_DATA_ENABLE);
2813 /* refresh the registration status */
2814 _hayes_request_type(hayes, channel,
2815 HAYES_REQUEST_REGISTRATION_UNSOLLICITED_ENABLE);
2816 /* refresh the current call status */
2817 _hayes_trigger(hayes, MODEM_EVENT_TYPE_CALL);
2818 /* refresh the contact list */
2819 _hayes_request_type(hayes, channel, MODEM_REQUEST_CONTACT_LIST);
2820 /* refresh the message list */
2821 _hayes_request_type(hayes, channel, MODEM_REQUEST_MESSAGE_LIST);
2822 return status;
2823 }
2824
2825
2826 /* on_request_unsupported */
_on_request_unsupported(HayesCommand * command,HayesCommandStatus status,HayesChannel * channel)2827 static HayesCommandStatus _on_request_unsupported(HayesCommand * command,
2828 HayesCommandStatus status, HayesChannel * channel)
2829 {
2830 /* FIXME report an unsupported event with the result of the command */
2831 return _on_request_generic(command, status, channel);
2832 }
2833
2834
2835 /* on_code_call_error */
_on_code_call_error(HayesChannel * channel,char const * answer)2836 static void _on_code_call_error(HayesChannel * channel, char const * answer)
2837 {
2838 Hayes * hayes = channel->hayes;
2839 HayesCommand * command = (channel->queue != NULL)
2840 ? channel->queue->data : NULL;
2841 (void) answer;
2842
2843 if(command != NULL)
2844 hayes_command_set_status(command, HCS_ERROR);
2845 _hayes_request_type(hayes, channel, HAYES_REQUEST_PHONE_ACTIVE);
2846 }
2847
2848
2849 /* on_code_cbc */
_on_code_cbc(HayesChannel * channel,char const * answer)2850 static void _on_code_cbc(HayesChannel * channel, char const * answer)
2851 {
2852 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_BATTERY_LEVEL];
2853 int res;
2854 unsigned int u;
2855 unsigned int v;
2856 double f;
2857
2858 if((res = sscanf(answer, "%u,%u", &u, &v)) != 2)
2859 return;
2860 event->battery_level.status = MODEM_BATTERY_STATUS_UNKNOWN;
2861 event->battery_level.charging = 0;
2862 if(u == 0)
2863 u = MODEM_BATTERY_STATUS_CONNECTED;
2864 else if(u == 1)
2865 u = MODEM_BATTERY_STATUS_CHARGING;
2866 else if(u == 2)
2867 u = MODEM_BATTERY_STATUS_NONE;
2868 else if(u == 3)
2869 u = MODEM_BATTERY_STATUS_ERROR;
2870 else
2871 u = MODEM_BATTERY_STATUS_UNKNOWN;
2872 switch((event->battery_level.status = u))
2873 {
2874 case MODEM_BATTERY_STATUS_CHARGING:
2875 event->battery_level.charging = 1;
2876 case MODEM_BATTERY_STATUS_CONNECTED:
2877 f = v;
2878 if(hayeschannel_has_quirks(channel,
2879 HAYES_QUIRK_BATTERY_70))
2880 f /= 70.0;
2881 else
2882 f /= 100.0;
2883 f = max(f, 0.0);
2884 event->battery_level.level = min(f, 1.0);
2885 break;
2886 default:
2887 event->battery_level.level = 0.0 / 0.0;
2888 break;
2889 }
2890 }
2891
2892
2893 /* on_code_cfun */
_on_code_cfun(HayesChannel * channel,char const * answer)2894 static void _on_code_cfun(HayesChannel * channel, char const * answer)
2895 {
2896 Hayes * hayes = channel->hayes;
2897 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_STATUS];
2898 unsigned int u;
2899
2900 if(sscanf(answer, "%u", &u) != 1)
2901 return;
2902 switch(u)
2903 {
2904 case 1:
2905 /* report being online */
2906 event->status.status = MODEM_STATUS_ONLINE;
2907 break;
2908 case 4: /* antennas disabled */
2909 case 0: /* telephony disabled */
2910 default:
2911 /* FIXME this is maybe not the right event type */
2912 event->status.status = MODEM_STATUS_OFFLINE;
2913 break;
2914 }
2915 hayes->helper->event(hayes->helper->modem, event);
2916 }
2917
2918
2919 /* on_code_cgatt */
_on_code_cgatt(HayesChannel * channel,char const * answer)2920 static void _on_code_cgatt(HayesChannel * channel, char const * answer)
2921 {
2922 Hayes * hayes = channel->hayes;
2923 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
2924 unsigned int u;
2925
2926 if(sscanf(answer, "%u", &u) != 1)
2927 return;
2928 free(channel->registration_media);
2929 channel->registration_media = NULL;
2930 if(u == 1)
2931 event->registration.media = "GPRS";
2932 else
2933 event->registration.media = NULL;
2934 /* this is usually worth an event */
2935 hayes->helper->event(hayes->helper->modem, event);
2936 }
2937
2938
2939 /* on_code_cgmi */
_on_code_cgmi(HayesChannel * channel,char const * answer)2940 static void _on_code_cgmi(HayesChannel * channel, char const * answer)
2941 {
2942 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MODEL];
2943 char * p;
2944
2945 if(answer[0] == '\0' || strcmp(answer, "OK") == 0
2946 || (p = strdup(answer)) == NULL) /* XXX report error? */
2947 return;
2948 free(channel->model_vendor);
2949 channel->model_vendor = p;
2950 event->model.vendor = p;
2951 }
2952
2953
2954 /* on_code_cgmm */
_on_code_cgmm(HayesChannel * channel,char const * answer)2955 static void _on_code_cgmm(HayesChannel * channel, char const * answer)
2956 {
2957 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MODEL];
2958 char * p;
2959
2960 if(answer[0] == '\0' || strcmp(answer, "OK") == 0
2961 || (p = strdup(answer)) == NULL) /* XXX report error? */
2962 return;
2963 free(channel->model_name);
2964 channel->model_name = p;
2965 event->model.name = p;
2966 hayeschannel_set_quirks(channel, hayes_quirks(event->model.vendor, p));
2967 }
2968
2969
2970 /* on_code_cgmr */
_on_code_cgmr(HayesChannel * channel,char const * answer)2971 static void _on_code_cgmr(HayesChannel * channel, char const * answer)
2972 {
2973 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MODEL];
2974 char * p;
2975
2976 if(answer[0] == '\0' || strcmp(answer, "OK") == 0
2977 || (p = strdup(answer)) == NULL) /* XXX report error? */
2978 return;
2979 free(channel->model_version);
2980 channel->model_version = p;
2981 event->model.version = p;
2982 }
2983
2984
2985 /* on_code_cgsn */
_on_code_cgsn(HayesChannel * channel,char const * answer)2986 static void _on_code_cgsn(HayesChannel * channel, char const * answer)
2987 {
2988 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MODEL];
2989 char * p;
2990
2991 if(answer[0] == '\0' || strcmp(answer, "OK") == 0
2992 || (p = strdup(answer)) == NULL) /* XXX report error? */
2993 return;
2994 free(channel->model_serial);
2995 channel->model_serial = p;
2996 event->model.serial = p;
2997 }
2998
2999
3000 /* on_code_cimi */
_on_code_cimi(HayesChannel * channel,char const * answer)3001 static void _on_code_cimi(HayesChannel * channel, char const * answer)
3002 {
3003 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MODEL];
3004 char * p;
3005
3006 if(answer[0] == '\0' || strcmp(answer, "OK") == 0
3007 || (p = strdup(answer)) == NULL) /* XXX report error? */
3008 return;
3009 free(channel->model_identity);
3010 channel->model_identity = p;
3011 event->model.identity = p;
3012 }
3013
3014
3015 /* on_code_clip */
_on_code_clip(HayesChannel * channel,char const * answer)3016 static void _on_code_clip(HayesChannel * channel, char const * answer)
3017 {
3018 Hayes * hayes = channel->hayes;
3019 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CALL];
3020 char buf[32];
3021 unsigned int u;
3022
3023 if(sscanf(answer, "\"%31[^\"]\",%u", buf, &u) != 2)
3024 return;
3025 buf[sizeof(buf) - 1] = '\0';
3026 free(channel->call_number);
3027 switch(u)
3028 {
3029 case 145:
3030 if((channel->call_number = malloc(sizeof(buf) + 1))
3031 == NULL)
3032 break;
3033 snprintf(channel->call_number, sizeof(buf) + 1, "%s%s",
3034 "+", buf);
3035 break;
3036 default:
3037 channel->call_number = strdup(buf);
3038 break;
3039 }
3040 /* this is always an unsollicited event */
3041 hayes->helper->event(hayes->helper->modem, event);
3042 }
3043
3044
3045 /* on_code_cme_error */
3046 static void _cme_error_registration(HayesChannel * channel, char const * error);
3047
_on_code_cme_error(HayesChannel * channel,char const * answer)3048 static void _on_code_cme_error(HayesChannel * channel, char const * answer)
3049 {
3050 const guint timeout = 5000;
3051 Hayes * hayes = channel->hayes;
3052 ModemPluginHelper * helper = hayes->helper;
3053 /* XXX ugly */
3054 HayesCommand * command = (channel->queue != NULL)
3055 ? channel->queue->data : NULL;
3056 unsigned int u;
3057 HayesCommand * p;
3058 ModemEvent * event;
3059
3060 if(command != NULL)
3061 hayes_command_set_status(command, HCS_ERROR);
3062 if(sscanf(answer, "%u", &u) != 1)
3063 return;
3064 switch(u)
3065 {
3066 case 10: /* SIM not inserted */
3067 /* FIXME also display an icon in the UI */
3068 _cme_error_registration(channel, "SIM not inserted");
3069 break;
3070 case 11: /* SIM PIN required */
3071 _on_code_cpin(channel, "SIM PIN");
3072 _hayes_trigger(hayes, MODEM_EVENT_TYPE_AUTHENTICATION);
3073 break;
3074 case 12: /* SIM PUK required */
3075 _on_code_cpin(channel, "SIM PUK");
3076 _hayes_trigger(hayes, MODEM_EVENT_TYPE_AUTHENTICATION);
3077 break;
3078 case 100: /* unknown error */
3079 if(hayeschannel_has_quirks(channel,
3080 HAYES_QUIRK_REPEAT_ON_UNKNOWN_ERROR) == 0)
3081 break;
3082 /* fallback */
3083 case 14: /* SIM busy */
3084 /* repeat the command */
3085 if(command == NULL)
3086 break;
3087 if((p = hayes_command_new_copy(command)) == NULL)
3088 break;
3089 hayes_command_set_data(p,
3090 hayes_command_get_data(command));
3091 hayes_command_set_data(command, NULL);
3092 channel->queue_timeout = g_slist_append(
3093 channel->queue_timeout, p);
3094 if(channel->source == 0)
3095 channel->source = g_timeout_add(timeout,
3096 _on_queue_timeout, channel);
3097 break;
3098 case 30: /* No network service */
3099 _cme_error_registration(channel, "No network service");
3100 break;
3101 case 31: /* Network timeout */
3102 event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
3103 event->registration.signal = 0.0 / 0.0;
3104 helper->event(helper->modem, event);
3105 break;
3106 case 32: /* emergency calls only */
3107 event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
3108 free(channel->registration_media);
3109 channel->registration_media = NULL;
3110 event->registration.media = NULL;
3111 free(channel->registration_operator);
3112 channel->registration_operator = NULL;
3113 event->registration._operator = "SOS";
3114 event->registration.status
3115 = MODEM_REGISTRATION_STATUS_REGISTERED;
3116 helper->event(helper->modem, event);
3117 /* verify the SIM card */
3118 _hayes_request_type(hayes, channel,
3119 HAYES_REQUEST_SIM_PIN_VALID);
3120 break;
3121 case 112: /* Location area not allowed */
3122 case 113: /* Roaming not allowed in this location area */
3123 event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
3124 event->registration.status
3125 = MODEM_REGISTRATION_STATUS_DENIED;
3126 helper->event(helper->modem, event);
3127 break;
3128 case 262: /* SIM blocked */
3129 _cme_error_registration(channel, "SIM blocked");
3130 break;
3131 default: /* FIXME implement the rest */
3132 case 3: /* Operation not allowed */
3133 case 4: /* Operation not supported */
3134 case 16: /* Incorrect SIM PIN/PUK */
3135 case 20: /* Memory full */
3136 case 107: /* GPRS services not allowed */
3137 case 148: /* Unspecified GPRS error */
3138 case 263: /* Invalid block */
3139 break;
3140 }
3141 }
3142
_cme_error_registration(HayesChannel * channel,char const * error)3143 static void _cme_error_registration(HayesChannel * channel, char const * error)
3144 {
3145 Hayes * hayes = channel->hayes;
3146 ModemEvent * event;
3147 ModemPluginHelper * helper = hayes->helper;
3148
3149 /* update the authentication status */
3150 event = &channel->events[MODEM_EVENT_TYPE_AUTHENTICATION];
3151 free(channel->authentication_error);
3152 channel->authentication_error = NULL;
3153 event->authentication.error = error;
3154 /* report the registration error */
3155 event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
3156 free(channel->registration_media);
3157 channel->registration_media = NULL;
3158 event->registration.media = NULL;
3159 free(channel->registration_operator);
3160 channel->registration_operator = NULL;
3161 event->registration._operator = NULL;
3162 event->registration.signal = 0.0 / 0.0;
3163 event->registration.status = MODEM_REGISTRATION_STATUS_DENIED;
3164 helper->event(helper->modem, event);
3165 }
3166
3167
3168 /* on_code_cmgl */
_on_code_cmgl(HayesChannel * channel,char const * answer)3169 static void _on_code_cmgl(HayesChannel * channel, char const * answer)
3170 {
3171 Hayes * hayes = channel->hayes;
3172 /* XXX ugly */
3173 HayesCommand * command = (channel->queue != NULL)
3174 ? channel->queue->data : NULL;
3175 ModemRequest request;
3176 unsigned int id;
3177 unsigned int u;
3178 HayesRequestMessageData * data;
3179 ModemMessageFolder folder = MODEM_MESSAGE_FOLDER_UNKNOWN;
3180 ModemMessageStatus status = MODEM_MESSAGE_STATUS_READ;
3181
3182 /* XXX we could already be reading the message at this point */
3183 if(sscanf(answer, "%u,%u,%u,%u", &id, &u, &u, &u) != 4
3184 && sscanf(answer, "%u,%u,,%u", &id, &u, &u) != 3)
3185 /* XXX we may be stuck in PDU mode at this point */
3186 return;
3187 request.type = MODEM_REQUEST_MESSAGE;
3188 request.message.id = id;
3189 if(command != NULL && (data = hayes_command_get_data(command)) != NULL)
3190 {
3191 folder = data->folder;
3192 status = data->status;
3193 }
3194 if((data = malloc(sizeof(*data))) != NULL)
3195 {
3196 data->id = id;
3197 data->folder = folder;
3198 data->status = status;
3199 }
3200 if(_hayes_request_channel(hayes, channel, &request, data) != 0)
3201 free(data);
3202 }
3203
3204
3205 /* on_code_cmgr */
3206 static char * _cmgr_pdu_parse(char const * pdu, time_t * timestamp,
3207 char * number, ModemMessageEncoding * encoding,
3208 size_t * length);
3209 static char * _cmgr_pdu_parse_encoding_data(char const * pdu, size_t len,
3210 size_t i, size_t hdr, ModemMessageEncoding * encoding,
3211 size_t * length);
3212 static char * _cmgr_pdu_parse_encoding_default(char const * pdu, size_t len,
3213 size_t i, size_t hdr, ModemMessageEncoding * encoding,
3214 size_t * length);
3215 static void _cmgr_pdu_parse_number(unsigned int type, char const * number,
3216 size_t length, char * buf);
3217 static time_t _cmgr_pdu_parse_timestamp(char const * timestamp);
3218
_on_code_cmgr(HayesChannel * channel,char const * answer)3219 static void _on_code_cmgr(HayesChannel * channel, char const * answer)
3220 {
3221 Hayes * hayes = channel->hayes;
3222 /* XXX ugly */
3223 HayesCommand * command = (channel->queue != NULL)
3224 ? channel->queue->data : NULL;
3225 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MESSAGE];
3226 char buf[32];
3227 char number[32];
3228 char date[32];
3229 struct tm t;
3230 unsigned int mbox;
3231 unsigned int alpha = 0;
3232 unsigned int length;
3233 char * p;
3234 HayesRequestMessageData * data;
3235
3236 /* text mode support */
3237 if(sscanf(answer, "\"%31[^\"]\",\"%31[^\"]\",,\"%31[^\"]\"", buf,
3238 number, date) == 3)
3239 {
3240 number[sizeof(number) - 1] = '\0';
3241 string_delete(channel->message_number);
3242 channel->message_number = strdup(number);
3243 event->message.number = channel->message_number;
3244 date[sizeof(date) - 1] = '\0';
3245 memset(&t, 0, sizeof(t));
3246 if(strptime(date, "%y/%m/%d,%H:%M:%S", &t) == NULL)
3247 /* XXX also parse the timezone? */
3248 localtime_r(NULL, &t);
3249 event->message.date = mktime(&t);
3250 event->message.length = 0;
3251 return; /* we need to wait for the next line */
3252 }
3253 /* PDU mode support */
3254 if(sscanf(answer, "%u,%u,%u", &mbox, &alpha, &length) == 3
3255 || sscanf(answer, "%u,,%u", &mbox, &length) == 2)
3256 return; /* we need to wait for the next line */
3257 /* message content */
3258 if(event->message.length == 0) /* XXX assumes this is text mode */
3259 {
3260 /* FIXME guarantee this would not happen */
3261 if(command == NULL || (data = hayes_command_get_data(command))
3262 == NULL)
3263 return;
3264 event->message.id = data->id;
3265 event->message.folder = data->folder;
3266 event->message.status = data->status;
3267 event->message.encoding = MODEM_MESSAGE_ENCODING_UTF8;
3268 event->message.content = answer;
3269 event->message.length = strlen(answer);
3270 hayes->helper->event(hayes->helper->modem, event);
3271 return;
3272 }
3273 if((p = _cmgr_pdu_parse(answer, &event->message.date, number,
3274 &event->message.encoding,
3275 &event->message.length)) == NULL)
3276 return;
3277 /* FIXME guarantee this would not happen */
3278 if(command == NULL || (data = hayes_command_get_data(command)) == NULL)
3279 return;
3280 event->message.id = data->id;
3281 event->message.folder = data->folder;
3282 event->message.status = data->status;
3283 event->message.number = number; /* XXX */
3284 event->message.content = p;
3285 hayes->helper->event(hayes->helper->modem, event);
3286 free(p);
3287 }
3288
_cmgr_pdu_parse(char const * pdu,time_t * timestamp,char * number,ModemMessageEncoding * encoding,size_t * length)3289 static char * _cmgr_pdu_parse(char const * pdu, time_t * timestamp,
3290 char * number, ModemMessageEncoding * encoding, size_t * length)
3291 {
3292 size_t len;
3293 unsigned int smscl;
3294 unsigned int tp;
3295 unsigned int hdr;
3296 unsigned int addrl;
3297 unsigned int pid;
3298 unsigned int dcs;
3299 unsigned int datal;
3300 unsigned int u;
3301 char const * q;
3302 size_t i;
3303
3304 #ifdef DEBUG
3305 fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, pdu);
3306 #endif
3307 len = strlen(pdu);
3308 if(sscanf(pdu, "%02X", &smscl) != 1) /* SMSC length */
3309 return NULL;
3310 if((smscl * 2) + 2 > len)
3311 return NULL;
3312 q = pdu + (smscl * 2) + 2;
3313 if(sscanf(q, "%02X", &tp) != 1)
3314 return NULL;
3315 if((tp & 0x03) != 0x00) /* TP-MTI not SMS-DELIVER */
3316 return NULL;
3317 hdr = ((tp & 0x40) == 0x40) ? 1 : 0; /* TP-UDHI header present */
3318 if((smscl * 2) + 4 > len)
3319 return NULL;
3320 q = pdu + (smscl * 2) + 4;
3321 if(sscanf(q, "%02X", &addrl) != 1) /* address length */
3322 return NULL;
3323 if((smscl * 2) + 6 > len)
3324 return NULL;
3325 q = pdu + (smscl * 2) + 6;
3326 if(sscanf(q, "%02X", &u) != 1) /* type of address */
3327 return NULL;
3328 /* FIXME this probably depends on the type of address */
3329 if(addrl % 2 == 1)
3330 addrl++;
3331 if((smscl * 2) + 2 + 4 + addrl + 2 > len)
3332 return NULL;
3333 _cmgr_pdu_parse_number(u, q + 2, addrl, number);
3334 q = pdu + (smscl * 2) + 2 + 4 + addrl + 2;
3335 if(sscanf(q, "%02X", &pid) != 1) /* PID */
3336 return NULL;
3337 if((smscl * 2) + 2 + 4 + addrl + 4 > len)
3338 return NULL;
3339 q = pdu + (smscl * 2) + 2 + 4 + addrl + 4;
3340 if(sscanf(q, "%02X", &dcs) != 1) /* DCS */
3341 return NULL;
3342 if((smscl * 2) + 2 + 4 + addrl + 6 > len)
3343 return NULL;
3344 q = pdu + (smscl * 2) + 2 + 4 + addrl + 6;
3345 if(timestamp != NULL)
3346 *timestamp = _cmgr_pdu_parse_timestamp(q);
3347 if((smscl * 2) + 2 + 4 + addrl + 6 + 14 > len)
3348 return NULL;
3349 q = pdu + (smscl * 2) + 2 + 4 + addrl + 6 + 14;
3350 if(sscanf(q, "%02X", &datal) != 1) /* data length */
3351 return NULL;
3352 /* XXX check the data length */
3353 if((i = (smscl * 2) + 2 + 4 + addrl + 6 + 16) > len)
3354 return NULL;
3355 if(hdr != 0 && sscanf(&pdu[i], "%02X", &hdr) != 1)
3356 return NULL;
3357 if(dcs == 0x00)
3358 return _cmgr_pdu_parse_encoding_default(pdu, len, i, hdr,
3359 encoding, length);
3360 if(dcs == 0x04)
3361 return _cmgr_pdu_parse_encoding_data(pdu, len, i, hdr,
3362 encoding, length);
3363
3364 return NULL;
3365 }
3366
_cmgr_pdu_parse_encoding_data(char const * pdu,size_t len,size_t i,size_t hdr,ModemMessageEncoding * encoding,size_t * length)3367 static char * _cmgr_pdu_parse_encoding_data(char const * pdu, size_t len,
3368 size_t i, size_t hdr, ModemMessageEncoding * encoding,
3369 size_t * length)
3370 {
3371 unsigned char * p;
3372 size_t j;
3373 unsigned int u;
3374
3375 #ifdef DEBUG
3376 fprintf(stderr, "DEBUG: %s()\n", __func__);
3377 #endif
3378 if((p = malloc(len - i + 1)) == NULL) /* XXX 2 times big enough? */
3379 return NULL;
3380 /* FIXME actually parse the header */
3381 if(hdr != 0)
3382 i += 2 + (hdr * 2);
3383 for(j = 0; i + 1 < len; i+=2)
3384 {
3385 if(sscanf(&pdu[i], "%02X", &u) != 1)
3386 {
3387 free(p);
3388 return NULL;
3389 }
3390 p[j++] = u;
3391 }
3392 *encoding = MODEM_MESSAGE_ENCODING_DATA;
3393 *length = j;
3394 p[j] = '\0';
3395 return (char *)p;
3396 }
3397
_cmgr_pdu_parse_encoding_default(char const * pdu,size_t len,size_t i,size_t hdr,ModemMessageEncoding * encoding,size_t * length)3398 static char * _cmgr_pdu_parse_encoding_default(char const * pdu, size_t len,
3399 size_t i, size_t hdr, ModemMessageEncoding * encoding,
3400 size_t * length)
3401 {
3402 unsigned char * p;
3403 size_t j;
3404 unsigned char rest;
3405 int shift = 0;
3406 char const * q;
3407 unsigned int u;
3408 unsigned char byte;
3409 char * r;
3410
3411 #ifdef DEBUG
3412 fprintf(stderr, "DEBUG: %s(%lu, %lu)\n", __func__, i, hdr);
3413 #endif
3414 if((p = malloc(len - i + 1)) == NULL)
3415 return NULL;
3416 if(hdr != 0)
3417 {
3418 /* FIXME actually parse the header */
3419 i += 2 + (hdr * 2);
3420 shift = (hdr + 1) % 7;
3421 }
3422 p[0] = '\0';
3423 for(j = 0, rest = 0; i + 1 < len; i+=2)
3424 {
3425 q = &pdu[i];
3426 if(sscanf(q, "%02X", &u) != 1)
3427 break; /* FIXME report an error instead? */
3428 byte = u;
3429 p[j] = (((byte << (shift + 1)) >> (shift + 1)) << shift) & 0x7f;
3430 p[j] |= rest;
3431 p[j] = _hayes_convert_gsm_to_iso(p[j]);
3432 /* ignore the first character if there is a header */
3433 if(hdr == 0 || j != 0)
3434 j++;
3435 rest = (byte >> (7 - shift)) & 0x7f;
3436 if(++shift == 7)
3437 {
3438 shift = 0;
3439 p[j++] = rest;
3440 rest = 0;
3441 }
3442 }
3443 *encoding = MODEM_MESSAGE_ENCODING_UTF8;
3444 if((r = g_convert((char *)p, j, "UTF-8", "ISO-8859-1", NULL, NULL,
3445 NULL)) != NULL)
3446 {
3447 free(p);
3448 p = (unsigned char *)r;
3449 j = strlen(r);
3450 }
3451 *length = j;
3452 return (char *)p;
3453 }
3454
3455
_cmgr_pdu_parse_number(unsigned int type,char const * number,size_t length,char * buf)3456 static void _cmgr_pdu_parse_number(unsigned int type, char const * number,
3457 size_t length, char * buf)
3458 {
3459 char * b = buf;
3460 size_t i;
3461
3462 #ifdef DEBUG
3463 fprintf(stderr, "DEBUG: %s()\n", __func__);
3464 #endif
3465 if(type == 0x91)
3466 *(b++) = '+';
3467 for(i = 0; i < length - 1 && i < 32 - 1; i+=2)
3468 {
3469 if((number[i] != 'F' && (number[i] < '0' || number[i] > '9'))
3470 || number[i + 1] < '0' || number[i + 1] > '9')
3471 break;
3472 b[i] = number[i + 1];
3473 if((b[i + 1] = number[i]) == 'F')
3474 b[i + 1] = '\0';
3475 }
3476 b[i] = '\0';
3477 #ifdef DEBUG
3478 fprintf(stderr, "DEBUG: %s(\"%s\", %lu) => \"%s\"\n", __func__, number,
3479 (unsigned long)length, b);
3480 #endif
3481 }
3482
_cmgr_pdu_parse_timestamp(char const * timestamp)3483 static time_t _cmgr_pdu_parse_timestamp(char const * timestamp)
3484 {
3485 char const * p = timestamp;
3486 size_t i;
3487 struct tm tm;
3488 time_t t;
3489 #if defined(__NetBSD__)
3490 timezone_t tz;
3491 #endif
3492 #ifdef DEBUG
3493 char buf[32];
3494 #endif
3495
3496 #ifdef DEBUG
3497 fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, timestamp);
3498 #endif
3499 if(strlen(p) < 14)
3500 return 0;
3501 for(i = 0; i < 14; i++)
3502 if(p[i] < '0' || p[i] > '9')
3503 return 0;
3504 memset(&tm, 0, sizeof(tm));
3505 tm.tm_year = (p[0] - '0') + ((p[1] - '0') * 10);
3506 tm.tm_year = (tm.tm_year > 70) ? tm.tm_year : (100 + tm.tm_year);
3507 tm.tm_mon = (p[2] - '0') + ((p[3] - '0') * 10);
3508 if(tm.tm_mon > 0)
3509 tm.tm_mon--;
3510 tm.tm_mday = (p[4] - '0') + ((p[5] - '0') * 10);
3511 tm.tm_hour = (p[6] - '0') + ((p[7] - '0') * 10);
3512 tm.tm_min = (p[8] - '0') + ((p[9] - '0') * 10);
3513 tm.tm_sec = (p[10] - '0') + ((p[11] - '0') * 10);
3514 #ifdef DEBUG
3515 strftime(buf, sizeof(buf), "%d/%m/%Y %H:%M:%S", &tm);
3516 fprintf(stderr, "DEBUG: %s() => \"%s\"\n", __func__, buf);
3517 #endif
3518 #if !defined(__NetBSD__)
3519 t = mktime(&tm);
3520 #else
3521 /* XXX assumes UTC */
3522 tz = tzalloc("UTC");
3523 t = mktime_z(tz, &tm);
3524 tzfree(tz);
3525 #endif
3526 return t;
3527 }
3528
3529
3530 /* on_code_cmgs */
_on_code_cmgs(HayesChannel * channel,char const * answer)3531 static void _on_code_cmgs(HayesChannel * channel, char const * answer)
3532 {
3533 Hayes * hayes = channel->hayes;
3534 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_MESSAGE_SENT];
3535 unsigned int u;
3536
3537 if(sscanf(answer, "%u", &u) != 1)
3538 return;
3539 event->message_sent.error = NULL;
3540 event->message_sent.id = u;
3541 hayes->helper->event(hayes->helper->modem, event);
3542 }
3543
3544
3545 /* on_code_cms_error */
_on_code_cms_error(HayesChannel * channel,char const * answer)3546 static void _on_code_cms_error(HayesChannel * channel, char const * answer)
3547 {
3548 const guint timeout = 5000;
3549 Hayes * hayes = channel->hayes;
3550 HayesCommand * command = (channel->queue != NULL)
3551 ? channel->queue->data : NULL;
3552 unsigned int u;
3553 HayesCommand * p;
3554
3555 if(command != NULL)
3556 hayes_command_set_status(command, HCS_ERROR);
3557 if(sscanf(answer, "%u", &u) != 1)
3558 return;
3559 switch(u)
3560 {
3561 case 311: /* SIM PIN required */
3562 _on_code_cpin(channel, "SIM PIN");
3563 _hayes_trigger(hayes, MODEM_EVENT_TYPE_AUTHENTICATION);
3564 break;
3565 case 316: /* SIM PUK required */
3566 _on_code_cpin(channel, "SIM PUK");
3567 _hayes_trigger(hayes, MODEM_EVENT_TYPE_AUTHENTICATION);
3568 break;
3569 case 500: /* Unknown error */
3570 if(hayeschannel_has_quirks(channel,
3571 HAYES_QUIRK_REPEAT_ON_UNKNOWN_ERROR) == 0)
3572 break;
3573 /* fallback */
3574 case 314: /* SIM busy */
3575 /* repeat the command */
3576 /* FIXME duplicated from _on_code_cme_error() */
3577 if(command == NULL)
3578 break;
3579 if((p = hayes_command_new_copy(command)) == NULL)
3580 break;
3581 hayes_command_set_data(p,
3582 hayes_command_get_data(command));
3583 hayes_command_set_data(command, NULL);
3584 channel->queue_timeout = g_slist_append(
3585 channel->queue_timeout, p);
3586 if(channel->source == 0)
3587 channel->source = g_timeout_add(timeout,
3588 _on_queue_timeout, channel);
3589 break;
3590 case 303: /* Operation not supported */
3591 case 321: /* Invalid memory index */
3592 default: /* FIXME implement the rest */
3593 break;
3594 }
3595 }
3596
3597
3598 /* on_code_cmti */
_on_code_cmti(HayesChannel * channel,char const * answer)3599 static void _on_code_cmti(HayesChannel * channel, char const * answer)
3600 {
3601 Hayes * hayes = channel->hayes;
3602 char buf[32];
3603 unsigned int u;
3604 ModemRequest request;
3605
3606 if(sscanf(answer, "\"%31[^\"]\",%u", buf, &u) != 2)
3607 return;
3608 buf[sizeof(buf) - 1] = '\0';
3609 /* fetch the new message directly */
3610 memset(&request, 0, sizeof(request));
3611 request.type = MODEM_REQUEST_MESSAGE;
3612 request.message.id = u;
3613 _hayes_request(hayes, &request);
3614 }
3615
3616
3617 /* on_code_connect */
_on_code_connect(HayesChannel * channel,char const * answer)3618 static void _on_code_connect(HayesChannel * channel, char const * answer)
3619 {
3620 Hayes * hayes = channel->hayes;
3621 ModemPluginHelper * helper = hayes->helper;
3622 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CONNECTION];
3623 HayesCommand * command = (channel->queue != NULL)
3624 ? channel->queue->data : NULL;
3625 char * argv[] = { "/usr/sbin/pppd", "pppd", "call", "phone",
3626 "user", "", "password", "", NULL };
3627 char const * p;
3628 gboolean res;
3629 const GSpawnFlags flags = G_SPAWN_FILE_AND_ARGV_ZERO;
3630 int wfd;
3631 int rfd;
3632 GError * error = NULL;
3633 (void) answer;
3634
3635 if(command != NULL) /* XXX else report error? */
3636 hayes_command_set_status(command, HCS_SUCCESS);
3637 _hayes_set_mode(hayes, channel, HAYESCHANNEL_MODE_DATA);
3638 /* pppd */
3639 if((p = helper->config_get(helper->modem, "pppd")) != NULL)
3640 {
3641 if((argv[0] = strdup(p)) == NULL)
3642 {
3643 hayes->helper->error(NULL, strerror(errno), 1);
3644 _hayes_reset(hayes);
3645 return;
3646 }
3647 argv[1] = basename(argv[0]);
3648 }
3649 /* username */
3650 if(channel->gprs_username != NULL)
3651 argv[5] = channel->gprs_username;
3652 /* password */
3653 if(channel->gprs_password != NULL)
3654 argv[7] = channel->gprs_password;
3655 res = g_spawn_async_with_pipes(NULL, argv, NULL, flags, NULL, NULL,
3656 NULL, &wfd, &rfd, NULL, &error);
3657 if(p != NULL)
3658 free(argv[0]);
3659 if(res == FALSE)
3660 {
3661 hayes->helper->error(NULL, error->message, 1);
3662 g_error_free(error);
3663 _hayes_reset(hayes);
3664 return;
3665 }
3666 channel->rd_ppp_channel = g_io_channel_unix_new(rfd);
3667 if(g_io_channel_set_encoding(channel->rd_ppp_channel, NULL, &error)
3668 != G_IO_STATUS_NORMAL)
3669 {
3670 hayes->helper->error(NULL, error->message, 1);
3671 g_error_free(error);
3672 error = NULL;
3673 }
3674 g_io_channel_set_buffered(channel->rd_ppp_channel, FALSE);
3675 channel->rd_ppp_source = g_io_add_watch(channel->rd_ppp_channel,
3676 G_IO_IN, _on_watch_can_read_ppp, channel);
3677 channel->wr_ppp_channel = g_io_channel_unix_new(wfd);
3678 if(g_io_channel_set_encoding(channel->wr_ppp_channel, NULL, &error)
3679 != G_IO_STATUS_NORMAL)
3680 {
3681 hayes->helper->error(NULL, error->message, 1);
3682 g_error_free(error);
3683 }
3684 g_io_channel_set_buffered(channel->wr_ppp_channel, FALSE);
3685 channel->wr_ppp_source = 0;
3686 event->connection.connected = 1;
3687 event->connection.in = 0;
3688 event->connection.out = 0;
3689 hayes->helper->event(hayes->helper->modem, event);
3690 }
3691
3692
3693 /* on_code_colp */
_on_code_colp(HayesChannel * channel,char const * answer)3694 static void _on_code_colp(HayesChannel * channel, char const * answer)
3695 {
3696 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CALL];
3697 char buf[32];
3698 unsigned int u;
3699
3700 if(sscanf(answer, "\"%31[^\"]\",%u", buf, &u) != 2)
3701 return; /* FIXME there may be different or more information */
3702 buf[sizeof(buf) - 1] = '\0';
3703 free(channel->call_number);
3704 switch(u)
3705 {
3706 case 145:
3707 if((channel->call_number = malloc(sizeof(buf) + 1))
3708 == NULL)
3709 break;
3710 snprintf(channel->call_number, sizeof(buf) + 1, "%s%s",
3711 "+", buf);
3712 break;
3713 default:
3714 channel->call_number = strdup(buf);
3715 break;
3716 }
3717 event->call.number = channel->call_number;
3718 }
3719
3720
3721 /* on_code_cops */
_on_code_cops(HayesChannel * channel,char const * answer)3722 static void _on_code_cops(HayesChannel * channel, char const * answer)
3723 {
3724 Hayes * hayes = channel->hayes;
3725 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
3726 unsigned int u;
3727 unsigned int v = 0;
3728 char buf[32] = "";
3729 unsigned int w;
3730
3731 if(sscanf(answer, "%u,%u,\"%31[^\"]\",%u", &u, &v, buf, &w) < 1)
3732 return;
3733 switch(u)
3734 {
3735 case 0:
3736 u = MODEM_REGISTRATION_MODE_AUTOMATIC;
3737 break;
3738 case 1:
3739 u = MODEM_REGISTRATION_MODE_MANUAL;
3740 break;
3741 case 2:
3742 u = MODEM_REGISTRATION_MODE_DISABLED;
3743 break;
3744 case 3: /* only for setting the format */
3745 default:
3746 u = event->registration.mode;
3747 break;
3748 }
3749 event->registration.mode = u;
3750 free(channel->registration_operator);
3751 channel->registration_operator = NULL;
3752 event->registration._operator = NULL;
3753 if(v != 0)
3754 /* force alphanumeric format */
3755 _hayes_request_type(hayes, channel,
3756 HAYES_REQUEST_OPERATOR_FORMAT_LONG);
3757 else
3758 {
3759 buf[sizeof(buf) - 1] = '\0';
3760 channel->registration_operator = strdup(buf);
3761 event->registration._operator = channel->registration_operator;
3762 }
3763 /* refresh registration data */
3764 _hayes_request_type(hayes, channel, MODEM_REQUEST_SIGNAL_LEVEL);
3765 _hayes_request_type(hayes, channel, HAYES_REQUEST_GPRS_ATTACHED);
3766 /* this is usually worth an event */
3767 hayes->helper->event(hayes->helper->modem, event);
3768 }
3769
3770
3771 /* on_code_cpas */
_on_code_cpas(HayesChannel * channel,char const * answer)3772 static void _on_code_cpas(HayesChannel * channel, char const * answer)
3773 {
3774 Hayes * hayes = channel->hayes;
3775 ModemPluginHelper * helper = hayes->helper;
3776 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CALL];
3777 unsigned int u;
3778
3779 if(sscanf(answer, "%u", &u) != 1)
3780 return;
3781 switch(u)
3782 {
3783 case 0:
3784 event->call.status = MODEM_CALL_STATUS_NONE;
3785 event->call.direction = MODEM_CALL_DIRECTION_NONE;
3786 /* report connection status */
3787 event = &channel->events[MODEM_EVENT_TYPE_CONNECTION];
3788 event->connection.connected = 0;
3789 event->connection.in = 0;
3790 event->connection.out = 0;
3791 helper->event(helper->modem, event);
3792 break;
3793 case 3:
3794 event->call.status = MODEM_CALL_STATUS_RINGING;
3795 /* report event */
3796 helper->event(helper->modem, event);
3797 break;
3798 case 4:
3799 event->call.status = MODEM_CALL_STATUS_ACTIVE;
3800 event->call.direction = MODEM_CALL_DIRECTION_NONE;
3801 break;
3802 case 2: /* XXX unknown */
3803 default:
3804 break;
3805 }
3806 }
3807
3808
3809 /* on_code_cpbr */
_on_code_cpbr(HayesChannel * channel,char const * answer)3810 static void _on_code_cpbr(HayesChannel * channel, char const * answer)
3811 {
3812 Hayes * hayes = channel->hayes;
3813 ModemRequest request;
3814 HayesRequestContactList list;
3815 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CONTACT];
3816 unsigned int u;
3817 unsigned int v;
3818 char number[32];
3819 char name[32];
3820 char * p;
3821
3822 if(sscanf(answer, "(%u-%u)", &u, &v) == 2)
3823 {
3824 memset(&request, 0, sizeof(request));
3825 request.type = HAYES_REQUEST_CONTACT_LIST;
3826 list.from = u;
3827 list.to = v;
3828 request.plugin.data = &list;
3829 _hayes_request(hayes, &request);
3830 return;
3831 }
3832 if(sscanf(answer, "%u,\"%31[^\"]\",%u,\"%31[^\"]\"",
3833 &event->contact.id, number, &u, name) != 4)
3834 return;
3835 switch(u)
3836 {
3837 case 145:
3838 if(number[0] == '+')
3839 break;
3840 /* prefix the number with a "+" */
3841 memmove(&number[1], number, sizeof(number) - 1);
3842 number[0] = '+';
3843 break;
3844 }
3845 number[sizeof(number) - 1] = '\0';
3846 free(channel->contact_number);
3847 channel->contact_number = strdup(number);
3848 event->contact.number = channel->contact_number;
3849 name[sizeof(name) - 1] = '\0';
3850 #if 1 /* FIXME is it really always in GSM? */
3851 _hayes_convert_gsm_string_to_iso(name);
3852 #endif
3853 if((p = g_convert(name, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL))
3854 != NULL)
3855 {
3856 snprintf(name, sizeof(name), "%s", p);
3857 g_free(p);
3858 }
3859 free(channel->contact_name);
3860 channel->contact_name = strdup(name);
3861 event->contact.name = channel->contact_name;
3862 event->contact.status = MODEM_CONTACT_STATUS_OFFLINE;
3863 /* send event */
3864 hayes->helper->event(hayes->helper->modem, event);
3865 }
3866
3867
3868 /* on_code_cpin */
_on_code_cpin(HayesChannel * channel,char const * answer)3869 static void _on_code_cpin(HayesChannel * channel, char const * answer)
3870 {
3871 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_AUTHENTICATION];
3872 char * p;
3873
3874 #ifdef DEBUG
3875 fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, answer);
3876 #endif
3877 if(strcmp(answer, "READY") == 0)
3878 {
3879 event->authentication.status = MODEM_AUTHENTICATION_STATUS_OK;
3880 hayescommon_source_reset(&channel->authenticate_source);
3881 channel->authenticate_count = 0;
3882 }
3883 else if(strcmp(answer, "SIM PIN") == 0
3884 || strcmp(answer, "SIM PUK") == 0)
3885 {
3886 free(channel->authentication_name);
3887 p = strdup(answer);
3888 channel->authentication_name = p;
3889 event->authentication.name = p;
3890 event->authentication.method = MODEM_AUTHENTICATION_METHOD_PIN;
3891 event->authentication.status
3892 = MODEM_AUTHENTICATION_STATUS_REQUIRED;
3893 /* FIXME also provide remaining retries */
3894 }
3895 }
3896
3897
3898 /* on_code_creg */
_on_code_creg(HayesChannel * channel,char const * answer)3899 static void _on_code_creg(HayesChannel * channel, char const * answer)
3900 {
3901 Hayes * hayes = channel->hayes;
3902 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
3903 int res;
3904 unsigned int u[4] = { 0, 0, 0, 0 };
3905
3906 res = sscanf(answer, "%u,%u,%X,%X", &u[0], &u[1], &u[2], &u[3]);
3907 if(res == 1)
3908 res = sscanf(answer, "%u,\"%X\",\"%X\"", &u[1], &u[2], &u[3]);
3909 else if(res == 2)
3910 res = sscanf(answer, "%u,%u,\"%X\",\"%X\"", &u[0], &u[1], &u[2],
3911 &u[3]);
3912 else if(res == 3)
3913 res = sscanf(answer, "%u,%X,%X", &u[1], &u[2], &u[3]);
3914 if(res == 0)
3915 return;
3916 u[0] = event->registration.mode;
3917 event->registration.roaming = 0;
3918 switch(u[1])
3919 {
3920 case 0:
3921 u[0] = MODEM_REGISTRATION_MODE_DISABLED;
3922 u[1] = MODEM_REGISTRATION_STATUS_NOT_SEARCHING;
3923 break;
3924 case 1:
3925 if(u[0] != MODEM_REGISTRATION_MODE_MANUAL)
3926 u[0] = MODEM_REGISTRATION_MODE_AUTOMATIC;
3927 u[1] = MODEM_REGISTRATION_STATUS_REGISTERED;
3928 break;
3929 case 2:
3930 if(u[0] != MODEM_REGISTRATION_MODE_MANUAL)
3931 u[0] = MODEM_REGISTRATION_MODE_AUTOMATIC;
3932 u[1] = MODEM_REGISTRATION_STATUS_SEARCHING;
3933 break;
3934 case 3:
3935 u[1] = MODEM_REGISTRATION_STATUS_DENIED;
3936 break;
3937 case 5:
3938 if(u[0] != MODEM_REGISTRATION_MODE_MANUAL)
3939 u[0] = MODEM_REGISTRATION_MODE_AUTOMATIC;
3940 u[1] = MODEM_REGISTRATION_STATUS_REGISTERED;
3941 event->registration.roaming = 1;
3942 break;
3943 case 4: /* unknown */
3944 default:
3945 #ifdef DEBUG
3946 if(u[1] != 4)
3947 fprintf(stderr, "DEBUG: %s() Unknown CREG %u\n",
3948 __func__, u[1]);
3949 #endif
3950 u[0] = MODEM_REGISTRATION_MODE_UNKNOWN;
3951 u[1] = MODEM_REGISTRATION_STATUS_UNKNOWN;
3952 break;
3953 }
3954 event->registration.mode = u[0];
3955 switch((event->registration.status = u[1]))
3956 {
3957 case MODEM_REGISTRATION_STATUS_REGISTERED:
3958 /* refresh registration data */
3959 _hayes_request_type(hayes, channel,
3960 HAYES_REQUEST_OPERATOR);
3961 break;
3962 default:
3963 free(channel->registration_media);
3964 channel->registration_media = NULL;
3965 event->registration.media = NULL;
3966 free(channel->registration_operator);
3967 channel->registration_operator = NULL;
3968 event->registration._operator = NULL;
3969 event->registration.signal = 0.0 / 0.0;
3970 break;
3971 }
3972 /* this is usually an unsollicited event */
3973 hayes->helper->event(hayes->helper->modem, event);
3974 }
3975
3976
3977 /* on_code_cring */
_on_code_cring(HayesChannel * channel,char const * answer)3978 static void _on_code_cring(HayesChannel * channel, char const * answer)
3979 {
3980 Hayes * hayes = channel->hayes;
3981 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_CALL];
3982
3983 if(strcmp(answer, "VOICE") == 0)
3984 event->call.call_type = MODEM_CALL_TYPE_VOICE;
3985 event->call.status = MODEM_CALL_STATUS_RINGING;
3986 event->call.direction = MODEM_CALL_DIRECTION_INCOMING;
3987 event->call.number = "";
3988 /* this is always an unsollicited event */
3989 hayes->helper->event(hayes->helper->modem, event);
3990 }
3991
3992
3993 /* on_code_csq */
_on_code_csq(HayesChannel * channel,char const * answer)3994 static void _on_code_csq(HayesChannel * channel, char const * answer)
3995 {
3996 Hayes * hayes = channel->hayes;
3997 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_REGISTRATION];
3998 unsigned int u;
3999 unsigned int v;
4000
4001 if(sscanf(answer, "%u,%u", &u, &v) != 2)
4002 return;
4003 if(u > 31)
4004 event->registration.signal = 0.0 / 0.0;
4005 else if(u >= 20)
4006 event->registration.signal = 1.0;
4007 else
4008 {
4009 event->registration.signal = (u > 4) ? (u - 4) : 0;
4010 event->registration.signal = event->registration.signal / 16.0;
4011 }
4012 /* this is usually worth an event */
4013 hayes->helper->event(hayes->helper->modem, event);
4014 }
4015
4016
4017 /* on_code_cusd */
_on_code_cusd(HayesChannel * channel,char const * answer)4018 static void _on_code_cusd(HayesChannel * channel, char const * answer)
4019 {
4020 Hayes * hayes = channel->hayes;
4021 ModemEvent * event = &channel->events[MODEM_EVENT_TYPE_NOTIFICATION];
4022 unsigned int u;
4023 char buf[32];
4024
4025 if(sscanf(answer, "%u,\"%31[^\"]\",%u", &u, buf, &u) >= 2)
4026 {
4027 event->notification.ntype = MODEM_NOTIFICATION_TYPE_INFO;
4028 event->notification.title = NULL;
4029 buf[sizeof(buf) - 1] = '\0';
4030 event->notification.content = buf;
4031 hayes->helper->event(hayes->helper->modem, event);
4032 }
4033 }
4034
4035
4036 /* on_code_ext_error */
_on_code_ext_error(HayesChannel * channel,char const * answer)4037 static void _on_code_ext_error(HayesChannel * channel, char const * answer)
4038 {
4039 /* XXX ugly */
4040 HayesCommand * command = (channel->queue != NULL)
4041 ? channel->queue->data : NULL;
4042 unsigned int u;
4043
4044 if(command != NULL)
4045 hayes_command_set_status(command, HCS_ERROR);
4046 if(sscanf(answer, "%u", &u) != 1)
4047 return;
4048 switch(u)
4049 {
4050 case 0:
4051 default: /* FIXME implement */
4052 break;
4053 }
4054 }
4055
4056
4057 /* helpers */
4058 /* is_ussd_code */
_is_ussd_code(char const * number)4059 static int _is_ussd_code(char const * number)
4060 {
4061 if(number == NULL || number[0] != '*')
4062 return 0;
4063 for(number++; *number != '\0'; number++)
4064 if(*number == '#')
4065 {
4066 if(*(number + 1) == '\0')
4067 return 1;
4068 continue;
4069 }
4070 else if((*number >= '0' && *number <= '9') || *number == '*')
4071 continue;
4072 else
4073 return 0;
4074 return 0;
4075 }
4076