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