1 /*
2 
3   $Id$
4 
5   G N O K I I
6 
7   A Linux/Unix toolset and driver for the mobile phones.
8 
9   This file is part of gnokii.
10 
11   Gnokii is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation; either version 2 of the License, or
14   (at your option) any later version.
15 
16   Gnokii is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20 
21   You should have received a copy of the GNU General Public License
22   along with gnokii; if not, write to the Free Software
23   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 
25   Copyright (C) 1999-2000  Hugh Blemings & Pavel Janik ml.
26   Copyright (C) 2001-2004  Pawel Kot
27   Copyright (C) 2002-2004  BORBELY Zoltan
28 
29   This file provides a virtual modem or "AT" interface to the GSM phone by
30   calling code in gsm-api.c. Inspired by and in places copied from the Linux
31   kernel AT Emulator IDSN code by Fritz Elfert and others.
32 
33 */
34 
35 #include "config.h"
36 
37 #include <stdio.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <signal.h>
41 #include <grp.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <time.h>
47 #include <ctype.h>
48 
49 #ifndef WIN32
50 #  include <termios.h>
51 #endif
52 
53 #include "misc.h"
54 #include "gnokii.h"
55 #include "compat.h"
56 #include "data/at-emulator.h"
57 #include "data/datapump.h"
58 
59 #define MAX_LINE_LENGTH 256
60 
61 #define	MAX_CMD_BUFFERS	(2)
62 #define	CMD_BUFFER_LENGTH (100)
63 
64 /* Definition of some special Registers of AT-Emulator, pinched in
65    part from ISDN driver in Linux kernel */
66 #define REG_RINGATA   0
67 #define REG_RINGCNT   1
68 #define REG_ESC       2
69 #define REG_CR        3
70 #define REG_LF        4
71 #define REG_BS        5
72 #define	S22          22
73 #define S35          35
74 #define REG_CTRLZ   100
75 #define REG_ESCAPE  101
76 
77 #define REG_QUIET    14
78 #define BIT_QUIET     4
79 #define REG_VERBOSE  14
80 #define BIT_VERBOSE   8
81 #define REG_ECHO     14
82 #define BIT_ECHO      2
83 
84 
85 #define	MAX_MODEM_REGISTERS	102
86 
87 /* Message format definitions */
88 #define PDU_MODE      0
89 #define TEXT_MODE     1
90 #define INTERACT_MODE 2
91 
92 /* Global variables */
93 bool gn_atem_initialised = false;	/* Set to true once initialised */
94 extern bool CommandMode;
95 extern int ConnectCount;
96 
97 struct gn_statemachine *sm;
98 gn_data data;
99 
100 static gn_sms sms;
101 static gn_call_info callinfo;
102 static char imei[GN_IMEI_MAX_LENGTH], model[GN_MODEL_MAX_LENGTH], revision[GN_REVISION_MAX_LENGTH], manufacturer[GN_MANUFACTURER_MAX_LENGTH];
103 
104 /* Local variables */
105 static int	PtyRDFD;	/* File descriptor for reading and writing to/from */
106 static int	PtyWRFD;	/* pty interface - only different in debug mode. */
107 
108 static u8	ModemRegisters[MAX_MODEM_REGISTERS];
109 static char	CmdBuffer[MAX_CMD_BUFFERS][CMD_BUFFER_LENGTH];
110 static int	CurrentCmdBuffer;
111 static int	CurrentCmdBufferIndex;
112 static int	IncomingCallNo;
113 static int     MessageFormat;          /* Message Format (text or pdu) */
114 static int	CallerIDMode;
115 
116 	/* Current command parser */
117 static void 	(*Parser)(char *); /* Current command parser */
118 /* void 	(*Parser)(char *) = gn_atem_at_parse; */
119 
120 static gn_memory_type 	SMSType;
121 static int 	SMSNumber;
122 
123 /* If initialised in debug mode, stdin/out is used instead
124    of ptys for interface. */
gn_atem_initialise(int read_fd,int write_fd,struct gn_statemachine * vmsm)125 bool gn_atem_initialise(int read_fd, int write_fd, struct gn_statemachine *vmsm)
126 {
127 	PtyRDFD = read_fd;
128 	PtyWRFD = write_fd;
129 
130 	gn_data_clear(&data);
131 	memset(&sms, 0, sizeof(sms));
132 	memset(&callinfo, 0, sizeof(callinfo));
133 
134 	data.sms = &sms;
135 	data.call_info = &callinfo;
136 	data.manufacturer = manufacturer;
137 	data.model = model;
138 	data.revision = revision;
139 	data.imei = imei;
140 
141 	sm = vmsm;
142 
143 	/* Initialise command buffer variables */
144 	CurrentCmdBuffer = 0;
145 	CurrentCmdBufferIndex = 0;
146 
147 	/* Initialise registers */
148 	gn_atem_registers_init();
149 
150 	/* Initial parser is AT routine */
151 	Parser = gn_atem_at_parse;
152 
153 	/* Setup defaults for AT*C interpreter. */
154 	SMSNumber = 1;
155 	SMSType = GN_MT_ME;
156 
157 	/* Default message format is PDU */
158 	MessageFormat = PDU_MODE;
159 
160 	/* Set the call passup so that we get notified of incoming calls */
161 	data.call_notification = gn_atem_call_passup;
162 	gn_sm_functions(GN_OP_SetCallNotification, &data, sm);
163 
164 	/* query model, revision and imei */
165 	if (gn_sm_functions(GN_OP_Identify, &data, sm) != GN_ERR_NONE)
166 		return false;
167 
168 	/* We're ready to roll... */
169 	gn_atem_initialised = true;
170 	return (true);
171 }
172 
173 
174 /* Initialise the "registers" used by the virtual modem. */
gn_atem_registers_init(void)175 void	gn_atem_registers_init(void)
176 {
177 	memset(ModemRegisters, 0, sizeof(ModemRegisters));
178 
179 	ModemRegisters[REG_RINGATA] = 0;
180 	ModemRegisters[REG_RINGCNT] = 0;
181 	ModemRegisters[REG_ESC] = '+';
182 	ModemRegisters[REG_CR] = 13;
183 	ModemRegisters[REG_LF] = 10;
184 	ModemRegisters[REG_BS] = 8;
185 	ModemRegisters[S35] = 7;
186 	ModemRegisters[REG_ECHO] |= BIT_ECHO;
187 	ModemRegisters[REG_VERBOSE] |= BIT_VERBOSE;
188 	ModemRegisters[REG_CTRLZ] = 26;
189 	ModemRegisters[REG_ESCAPE] = 27;
190 
191 	CallerIDMode = 0;
192 }
193 
194 
gn_atem_hangup_phone(void)195 static void  gn_atem_hangup_phone(void)
196 {
197 	if (IncomingCallNo > 0) {
198 		rlp_user_request_set(Disc_Req, true);
199 		gn_sm_loop(10, sm);
200 	}
201 	if (IncomingCallNo > 0) {
202 		data.call_info->call_id = IncomingCallNo;
203 		gn_sm_functions(GN_OP_CancelCall, &data, sm);
204 		IncomingCallNo = -1;
205 	}
206 	dp_Initialise(PtyRDFD, PtyWRFD);
207 }
208 
209 
gn_atem_answer_phone(void)210 static void  gn_atem_answer_phone(void)
211 {
212 	/* For now we'll also initialise the datapump + rlp code again */
213 	dp_Initialise(PtyRDFD, PtyWRFD);
214 	data.call_notification = dp_CallPassup;
215 	gn_sm_functions(GN_OP_SetCallNotification, &data, sm);
216 	data.call_info->call_id = IncomingCallNo;
217 	gn_sm_functions(GN_OP_AnswerCall, &data, sm);
218 	CommandMode = false;
219 }
220 
221 
222 /* This gets called to indicate an incoming call */
gn_atem_call_passup(gn_call_status CallStatus,gn_call_info * CallInfo,struct gn_statemachine * state,void * callback_data)223 void gn_atem_call_passup(gn_call_status CallStatus, gn_call_info *CallInfo, struct gn_statemachine *state, void *callback_data)
224 {
225 	dprintf("gn_atem_call_passup called with %d\n", CallStatus);
226 
227 	switch (CallStatus) {
228 	case GN_CALL_Incoming:
229 		gn_atem_modem_result(MR_RING);
230 		IncomingCallNo = CallInfo->call_id;
231 		ModemRegisters[REG_RINGCNT]++;
232 		gn_atem_cid_out(CallInfo);
233 		if (ModemRegisters[REG_RINGATA] != 0) gn_atem_answer_phone();
234 		break;
235 	case GN_CALL_LocalHangup:
236 	case GN_CALL_RemoteHangup:
237 		IncomingCallNo = -1;
238 		break;
239 	default:
240 		break;
241 	}
242 }
243 
244 /* This gets called to output caller id info of incoming call */
gn_atem_cid_out(gn_call_info * CallInfo)245 void gn_atem_cid_out(gn_call_info *CallInfo)
246 {
247 	struct tm *now;
248 	time_t nowh;
249 	char buf[14]; /* 7 for "DATE = " + 4 digits + \n + \r + \0 */
250 
251 	nowh = time(NULL);
252 	now = localtime(&nowh);
253 
254 	switch (CallerIDMode) {
255 	case 0:	/* no output */
256 		break;
257 	case 1: /* formatted CID */
258 		snprintf(buf, sizeof(buf), "DATE = %02d%02d\r\n", now->tm_mon + 1, now->tm_mday);
259 		gn_atem_string_out(buf);
260 
261 		snprintf(buf, sizeof(buf), "TIME = %02d%02d\r\n", now->tm_hour, now->tm_min);
262 		gn_atem_string_out(buf);
263 
264 		/* TO DO: handle P and O numbers */
265 		gn_atem_string_out("NMBR = ");
266 		gn_atem_string_out(1 + CallInfo->number); /* skip leading "+" */
267 		gn_atem_string_out("\r\nNAME = ");
268 		gn_atem_string_out(CallInfo->name);
269 		gn_atem_string_out("\r\n");
270 
271 		/* FIX ME: do a real emulation of rings after the first one (at a lower level than this) */
272 		gn_atem_modem_result(MR_RING);
273 
274 		break;
275 
276 	}
277 }
278 
279 /* Handler called when characters received from serial port.
280    calls state machine code to process it. */
gn_atem_incoming_data_handle(char * buffer,int length)281 void	gn_atem_incoming_data_handle(char *buffer, int length)
282 {
283 	int count;
284 	unsigned char out_buf[3];
285 
286 	for (count = 0; count < length ; count++) {
287 
288 		/* If it's a command terminator character, parse what
289 		   we have so far then go to next buffer. */
290 		if (buffer[count] == ModemRegisters[REG_CR] ||
291 		    buffer[count] == ModemRegisters[REG_LF] ||
292 		    buffer[count] == ModemRegisters[REG_CTRLZ] ||
293 		    buffer[count] == ModemRegisters[REG_ESCAPE]) {
294 
295 			/* Echo character if appropriate. */
296 			if (buffer[count] == ModemRegisters[REG_CR] &&
297 				(ModemRegisters[REG_ECHO] & BIT_ECHO)) {
298 				gn_atem_string_out("\r\n");
299 			}
300 
301 			/* Save CTRL-Z and ESCAPE for the parser */
302 			if (buffer[count] == ModemRegisters[REG_CTRLZ] ||
303 			    buffer[count] == ModemRegisters[REG_ESCAPE])
304 				CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex++] = buffer[count];
305 
306 			CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex] = 0x00;
307 
308 			Parser(CmdBuffer[CurrentCmdBuffer]);
309 
310 			CurrentCmdBuffer++;
311 			if (CurrentCmdBuffer >= MAX_CMD_BUFFERS) {
312 				CurrentCmdBuffer = 0;
313 			}
314 			CurrentCmdBufferIndex = 0;
315 
316 		} else if (buffer[count] == ModemRegisters[REG_BS]) {
317 			if (CurrentCmdBufferIndex > 0) {
318 				/* Echo character if appropriate. */
319 				if (ModemRegisters[REG_ECHO] & BIT_ECHO) {
320 					gn_atem_string_out("\b \b");
321 				}
322 
323 				CurrentCmdBufferIndex--;
324 			}
325 		} else {
326 			/* Echo character if appropriate. */
327 			if (ModemRegisters[REG_ECHO] & BIT_ECHO) {
328 				out_buf[0] = buffer[count];
329 				out_buf[1] = 0;
330 				gn_atem_string_out((char *)out_buf);
331 			}
332 
333 			/* Collect it to command buffer */
334 			CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex++] = buffer[count];
335 			if (CurrentCmdBufferIndex >= CMD_BUFFER_LENGTH) {
336 				CurrentCmdBufferIndex = CMD_BUFFER_LENGTH;
337 			}
338 		}
339 	}
340 }
341 
342 
343 /* Parser for standard AT commands.  cmd_buffer must be null terminated. */
gn_atem_at_parse(char * cmd_buffer)344 void	gn_atem_at_parse(char *cmd_buffer)
345 {
346 	char *buf;
347 	int regno, val;
348 	char str[256];
349 
350 	if (!cmd_buffer[0]) return;
351 
352 	if (strncasecmp (cmd_buffer, "AT", 2) != 0) {
353 		gn_atem_modem_result(MR_ERROR);
354 		return;
355 	}
356 
357 	for (buf = &cmd_buffer[2]; *buf;) {
358 		switch (toupper(*buf)) {
359 
360 		case 'Z':
361 			/* Reset modem */
362 			buf++;
363 			switch (gn_atem_num_get(&buf)) {
364 			case -1:
365 			case 0:	/* reset and load stored profile 0 */
366 			case 1:	/* reset and load stored profile 1 */
367 				gn_atem_hangup_phone();
368 				gn_atem_registers_init();
369 				break;
370 			default:
371 				gn_atem_modem_result(MR_ERROR);
372 				return;
373 			}
374 			break;
375 
376 		case 'A':
377 		        /* Answer call */
378 			buf++;
379 			gn_atem_answer_phone();
380 			return;
381 			break;
382 
383 		case 'D':
384 			/* Dial Data :-) */
385 			/* FIXME - should parse this better */
386 			/* For now we'll also initialise the datapump + rlp code again */
387 			dp_Initialise(PtyRDFD, PtyWRFD);
388 			buf++;
389 			if (toupper(*buf) == 'T' || toupper(*buf) == 'P') buf++;
390 			while (*buf == ' ') buf++;
391 			data.call_notification = dp_CallPassup;
392 			gn_sm_functions(GN_OP_SetCallNotification, &data, sm);
393 			snprintf(data.call_info->number, sizeof(data.call_info->number), "%s", buf);
394 			if (ModemRegisters[S35] == 0)
395 				data.call_info->type = GN_CALL_DigitalData;
396 			else
397 				data.call_info->type = GN_CALL_NonDigitalData;
398 			data.call_info->send_number = GN_CALL_Default;
399 			CommandMode = false;
400 			if (gn_sm_functions(GN_OP_MakeCall, &data, sm) != GN_ERR_NONE) {
401 				CommandMode = true;
402 				dp_CallPassup(GN_CALL_RemoteHangup, NULL, NULL, NULL);
403 			} else {
404 				IncomingCallNo = data.call_info->call_id;
405 				gn_sm_loop(10, sm);
406 			}
407 			return;
408 			break;
409 
410 		case 'H':
411 			/* Hang Up */
412 			buf++;
413 			switch (gn_atem_num_get(&buf)) {
414 			case -1:
415 			case 0:	/* hook off the phone */
416 				gn_atem_hangup_phone();
417 				break;
418 			case 1:	/* hook on the phone */
419 				break;
420 			default:
421 				gn_atem_modem_result(MR_ERROR);
422 				return;
423 			}
424 			break;
425 
426 		case 'S':
427 			/* Change registers */
428 			buf++;
429 			regno = gn_atem_num_get(&buf);
430 			if (regno < 0 || regno >= MAX_MODEM_REGISTERS) {
431 				gn_atem_modem_result(MR_ERROR);
432 				return;
433 			}
434 			if (*buf == '=') {
435 				buf++;
436 				val = gn_atem_num_get(&buf);
437 				if (val < 0 || val > 255) {
438 					gn_atem_modem_result(MR_ERROR);
439 					return;
440 				}
441 				ModemRegisters[regno] = val;
442 			} else if (*buf == '?') {
443 				buf++;
444 				snprintf(str, sizeof(str), "%d\r\n", ModemRegisters[regno]);
445 				gn_atem_string_out(str);
446 			} else {
447 				gn_atem_modem_result(MR_ERROR);
448 				return;
449 			}
450 			break;
451 
452 		case 'E':
453 		        /* E - Turn Echo on/off */
454 			buf++;
455 			switch (gn_atem_num_get(&buf)) {
456 			case -1:
457 			case 0:
458 				ModemRegisters[REG_ECHO] &= ~BIT_ECHO;
459 				break;
460 			case 1:
461 				ModemRegisters[REG_ECHO] |= BIT_ECHO;
462 				break;
463 			default:
464 				gn_atem_modem_result(MR_ERROR);
465 				return;
466 			}
467 			break;
468 
469 		case 'Q':
470 		        /* Q - Turn Quiet on/off */
471 			buf++;
472 			switch (gn_atem_num_get(&buf)) {
473 			case -1:
474 			case 0:
475 				ModemRegisters[REG_QUIET] &= ~BIT_QUIET;
476 				break;
477 			case 1:
478 				ModemRegisters[REG_QUIET] |= BIT_QUIET;
479 				break;
480 			default:
481 				gn_atem_modem_result(MR_ERROR);
482 				return;
483 			}
484 			break;
485 
486 		case 'V':
487 		        /* V - Turn Verbose on/off */
488 			buf++;
489 			switch (gn_atem_num_get(&buf)) {
490 			case -1:
491 			case 0:
492 				ModemRegisters[REG_VERBOSE] &= ~BIT_VERBOSE;
493 				break;
494 			case 1:
495 				ModemRegisters[REG_VERBOSE] |= BIT_VERBOSE;
496 				break;
497 			default:
498 				gn_atem_modem_result(MR_ERROR);
499 				return;
500 			}
501 			break;
502 
503 		case 'X':
504 		        /* X - Set verbosity of the result messages */
505 			buf++;
506 			switch (gn_atem_num_get(&buf)) {
507 			case -1:
508 			case 0: val = 0x00; break;
509 			case 1: val = 0x40; break;
510 			case 2: val = 0x50; break;
511 			case 3: val = 0x60; break;
512 			case 4: val = 0x70; break;
513 			case 5: val = 0x10; break;
514 			default:
515 				gn_atem_modem_result(MR_ERROR);
516 				return;
517 			}
518 			ModemRegisters[S22] = (ModemRegisters[S22] & 0x8f) | val;
519 			break;
520 
521 		case 'I':
522 		        /* I - info */
523 			buf++;
524 			switch (gn_atem_num_get(&buf)) {
525 			case -1:
526 			case 0:	/* terminal id */
527 				snprintf(str, sizeof(str), "%d\r\n", ModemRegisters[39]);
528 				gn_atem_string_out(str);
529 				break;
530 			case 1:	/* serial number (IMEI) */
531 				snprintf(str, sizeof(str), "%s\r\n", imei);
532 				gn_atem_string_out(str);
533 				break;
534 			case 2: /* phone revision */
535 				snprintf(str, sizeof(str), "%s\r\n", revision);
536 				gn_atem_string_out(str);
537 				break;
538 			case 3: /* modem revision */
539 				gn_atem_string_out("gnokiid " VERSION "\r\n");
540 				break;
541 			case 4: /* OEM string */
542 				snprintf(str, sizeof(str), "%s %s\r\n", manufacturer, model);
543 				gn_atem_string_out(str);
544 				break;
545 			default:
546 				gn_atem_modem_result(MR_ERROR);
547 				return;
548 			}
549 			break;
550 
551 		  /* Handle AT* commands (Nokia proprietary I think) */
552 		case '*':
553 			buf++;
554 			if (!strcasecmp(buf, "NOKIATEST")) {
555 				gn_atem_modem_result(MR_OK); /* FIXME? */
556 				return;
557 			} else {
558 				if (!strcasecmp(buf, "C")) {
559 					gn_atem_modem_result(MR_OK);
560 					Parser = gn_atem_sms_parse;
561 					return;
562 				}
563 			}
564 			break;
565 
566 		/* + is the precursor to another set of commands */
567 		case '+':
568 			buf++;
569 			switch (toupper(*buf)) {
570 			case 'C':
571 				buf++;
572 				/* Returns true if error occured */
573 				if (gn_atem_command_plusc(&buf) == true) {
574 					gn_atem_modem_result(MR_ERROR);
575 					return;
576 				}
577 				break;
578 
579 			case 'G':
580 				buf++;
581 				/* Returns true if error occured */
582 				if (gn_atem_command_plusg(&buf) == true) {
583 					gn_atem_modem_result(MR_ERROR);
584 					return;
585 				}
586 				break;
587 
588 			default:
589 				gn_atem_modem_result(MR_ERROR);
590 				return;
591 			}
592 			break;
593 
594 		/* # is the precursor to another set of commands */
595 		case '#':
596 			buf++;
597 			/* Returns true if error occured */
598 			if (gn_atem_command_diesis(&buf) == true) {
599 				gn_atem_modem_result(MR_ERROR);
600 				return;
601 			}
602 			break;
603 
604 		default:
605 			gn_atem_modem_result(MR_ERROR);
606 			return;
607 		}
608 	}
609 
610 	gn_atem_modem_result(MR_OK);
611 }
612 
613 
gn_atem_sms_print(char * line,gn_sms * message,int mode)614 static void gn_atem_sms_print(char *line, gn_sms *message, int mode)
615 {
616 	switch (mode) {
617 	case INTERACT_MODE:
618 		gsprintf(line, MAX_LINE_LENGTH,
619 			_("\r\nDate/time: %d/%d/%d %d:%02d:%02d Sender: %s Msg Center: %s\r\nText: %s\r\n"),
620 			message->smsc_time.day, message->smsc_time.month, message->smsc_time.year,
621 			message->smsc_time.hour, message->smsc_time.minute, message->smsc_time.second,
622 			message->remote.number, message->smsc.number, message->user_data[0].u.text);
623 		break;
624 	case TEXT_MODE:
625 		if ((message->dcs.type == GN_SMS_DCS_GeneralDataCoding) &&
626 		    (message->dcs.u.general.alphabet == GN_SMS_DCS_8bit))
627 			gsprintf(line, MAX_LINE_LENGTH,
628 				_("\"%s\",\"%s\",,\"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"\r\n%s"),
629 				(message->status ? _("REC READ") : _("REC UNREAD")),
630 				message->remote.number,
631 				message->smsc_time.year, message->smsc_time.month, message->smsc_time.day,
632 				message->smsc_time.hour, message->smsc_time.minute, message->smsc_time.second,
633 				message->time.timezone, _("<Not implemented>"));
634 		else
635 			gsprintf(line, MAX_LINE_LENGTH,
636 				_("\"%s\",\"%s\",,\"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"\r\n%s"),
637 				(message->status ? _("REC READ") : _("REC UNREAD")),
638 				message->remote.number,
639 				message->smsc_time.year, message->smsc_time.month, message->smsc_time.day,
640 				message->smsc_time.hour, message->smsc_time.minute, message->smsc_time.second,
641 				message->time.timezone, message->user_data[0].u.text);
642 		break;
643 	case PDU_MODE:
644 		gsprintf(line, MAX_LINE_LENGTH, _("0,<Not implemented>"));
645 		break;
646 	default:
647 		gsprintf(line, MAX_LINE_LENGTH, _("<Unknown mode>"));
648 		break;
649 	}
650 }
651 
652 
gn_atem_sms_handle()653 static void gn_atem_sms_handle()
654 {
655 	gn_error	error;
656 	char		buffer[MAX_LINE_LENGTH];
657 
658 	data.sms->memory_type = SMSType;
659 	data.sms->number = SMSNumber;
660 	error = gn_sms_get(&data, sm);
661 
662 	switch (error) {
663 	case GN_ERR_NONE:
664 		gn_atem_sms_print(buffer, data.sms, INTERACT_MODE);
665 		gn_atem_string_out(buffer);
666 		break;
667 	default:
668 		gsprintf(buffer, MAX_LINE_LENGTH, _("\r\nNo message under number %d\r\n"), SMSNumber);
669 		gn_atem_string_out(buffer);
670 		break;
671 	}
672 	return;
673 }
674 
675 /* Parser for SMS interactive mode */
gn_atem_sms_parse(char * buff)676 void	gn_atem_sms_parse(char *buff)
677 {
678 	if (!strcasecmp(buff, "HELP")) {
679 		gn_atem_string_out(_("\r\nThe following commands work...\r\n"));
680 		gn_atem_string_out("DIR\r\n");
681 		gn_atem_string_out("EXIT\r\n");
682 		gn_atem_string_out("HELP\r\n");
683 		return;
684 	}
685 
686 	if (!strcasecmp(buff, "DIR")) {
687 		SMSNumber = 1;
688 		gn_atem_sms_handle();
689 		Parser = gn_atem_dir_parse;
690 		return;
691 	}
692 	if (!strcasecmp(buff, "EXIT")) {
693 		Parser = gn_atem_at_parse;
694 		gn_atem_modem_result(MR_OK);
695 		return;
696 	}
697 	gn_atem_modem_result(MR_ERROR);
698 }
699 
700 /* Parser for DIR sub mode of SMS interactive mode. */
gn_atem_dir_parse(char * buff)701 void	gn_atem_dir_parse(char *buff)
702 {
703 	switch (toupper(*buff)) {
704 		case 'P':
705 			SMSNumber--;
706 			gn_atem_sms_handle();
707 			return;
708 		case 'N':
709 			SMSNumber++;
710 			gn_atem_sms_handle();
711 			return;
712 		case 'D':
713 			data.sms->memory_type = SMSType;
714 			data.sms->number = SMSNumber;
715 			if (gn_sm_functions(GN_OP_DeleteSMS, &data, sm) == GN_ERR_NONE) {
716 				gn_atem_modem_result(MR_OK);
717 			} else {
718 				gn_atem_modem_result(MR_ERROR);
719 			}
720 			return;
721 		case 'Q':
722 			Parser= gn_atem_sms_parse;
723 			gn_atem_modem_result(MR_OK);
724 			return;
725 	}
726 	gn_atem_modem_result(MR_ERROR);
727 }
728 
729 /* Parser for entering message content (+CMGS) */
gn_atem_sms_parseText(char * buff)730 void	gn_atem_sms_parseText(char *buff)
731 {
732 	static int index = 0;
733 	int i, length;
734 	char buffer[MAX_LINE_LENGTH];
735 	gn_error error;
736 
737 	length = strlen(buff);
738 
739 	sms.user_data[0].type = GN_SMS_DATA_Text;
740 
741 	for (i = 0; i < length; i++) {
742 
743 		if (buff[i] == ModemRegisters[REG_CTRLZ]) {
744 			/* Exit SMS text mode with sending */
745 			sms.user_data[0].u.text[index] = 0;
746 			sms.user_data[0].length = index;
747 			index = 0;
748 			Parser = gn_atem_at_parse;
749 			dprintf("Sending SMS to %s (text: %s)\n", data.sms->remote.number, data.sms->user_data[0].u.text);
750 
751 			/* FIXME: set more SMS fields before sending */
752 			error = gn_sms_send(&data, sm);
753 
754 			if (error == GN_ERR_NONE) {
755 				gsprintf(buffer, MAX_LINE_LENGTH, "+CMGS: %d\r\n", data.sms->number);
756 				gn_atem_string_out(buffer);
757 				gn_atem_modem_result(MR_OK);
758 			} else {
759 				gn_atem_modem_result(MR_ERROR);
760 			}
761 			return;
762 		} else if (buff[i] == ModemRegisters[REG_ESCAPE]) {
763 			/* Exit SMS text mode without sending */
764 			sms.user_data[0].u.text[index] = 0;
765 			sms.user_data[0].length = index;
766 			index = 0;
767 			Parser = gn_atem_at_parse;
768 			gn_atem_modem_result(MR_OK);
769 			return;
770 		} else {
771 			/* Appent next char to message text */
772 			sms.user_data[0].u.text[index++] = buff[i];
773 		}
774 	}
775 
776 	/* reached the end of line so insert \n and wait for more */
777 	sms.user_data[0].u.text[index++] = '\n';
778 	gn_atem_string_out("\r\n> ");
779 }
780 
781 /* Handle AT+C commands, this is a quick hack together at this
782    stage. */
gn_atem_command_plusc(char ** buf)783 bool	gn_atem_command_plusc(char **buf)
784 {
785 	float		rflevel = -1;
786 	gn_rf_unit	rfunits = GN_RF_CSQ;
787 	char		buffer[MAX_LINE_LENGTH], buffer2[MAX_LINE_LENGTH];
788 	int		status, index;
789 	gn_error	error;
790 
791 	if (strncasecmp(*buf, "SQ", 2) == 0) {
792 		buf[0] += 2;
793 
794 		data.rf_unit = &rfunits;
795 		data.rf_level = &rflevel;
796 		if (gn_sm_functions(GN_OP_GetRFLevel, &data, sm) == GN_ERR_NONE) {
797 			gsprintf(buffer, MAX_LINE_LENGTH, "+CSQ: %0.0f, 99\r\n", *(data.rf_level));
798 			gn_atem_string_out(buffer);
799 			return (false);
800 		} else {
801 			return (true);
802 		}
803 	}
804 
805 	/* AT+CGMI is Manufacturer information for the ME (phone) so
806 	   it should be Nokia rather than gnokii... */
807 	if (strncasecmp(*buf, "GMI", 3) == 0) {
808 		buf[0] += 3;
809 		gn_atem_string_out(_("Nokia Mobile Phones\r\n"));
810 		return (false);
811 	}
812 
813 	/* AT+CGSN is IMEI */
814 	if (strncasecmp(*buf, "GSN", 3) == 0) {
815 		buf[0] += 3;
816 		snprintf(data.imei, GN_IMEI_MAX_LENGTH, "+CME ERROR: 0");
817 		if (gn_sm_functions(GN_OP_GetImei, &data, sm) == GN_ERR_NONE) {
818 			gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.imei);
819 			gn_atem_string_out(buffer);
820 			return (false);
821 		} else {
822 			return (true);
823 		}
824 	}
825 
826 	/* AT+CGMR is Revision (hardware) */
827 	if (strncasecmp(*buf, "GMR", 3) == 0) {
828 		buf[0] += 3;
829 		snprintf(data.revision, GN_REVISION_MAX_LENGTH, "+CME ERROR: 0");
830 		if (gn_sm_functions(GN_OP_GetRevision, &data, sm) == GN_ERR_NONE) {
831 			gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.revision);
832 			gn_atem_string_out(buffer);
833 			return (false);
834 		} else {
835 			return (true);
836 		}
837 	}
838 
839 	/* AT+CGMM is Model code  */
840 	if (strncasecmp(*buf, "GMM", 3) == 0) {
841 		buf[0] += 3;
842 		snprintf(data.model, GN_MODEL_MAX_LENGTH, "+CME ERROR: 0");
843 		if (gn_sm_functions(GN_OP_GetModel, &data, sm) == GN_ERR_NONE) {
844 			gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.model);
845 			gn_atem_string_out(buffer);
846 			return (false);
847 		} else {
848 			return (true);
849 		}
850 	}
851 
852 	/* AT+CMGD is deleting a message */
853 	if (strncasecmp(*buf, "MGD", 3) == 0) {
854 		buf[0] += 3;
855 		switch (**buf) {
856 		case '=':
857 			buf[0]++;
858 			index = atoi(*buf);
859 			buf[0] += strlen(*buf);
860 
861 			data.sms->memory_type = SMSType;
862 			data.sms->number = index;
863 			error = gn_sm_functions(GN_OP_DeleteSMS, &data, sm);
864 
865 			switch (error) {
866 			case GN_ERR_NONE:
867 				break;
868 			default:
869 				gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error);
870 				gn_atem_string_out(buffer);
871 				return (true);
872 			}
873 			break;
874 		default:
875 			return (true);
876 		}
877 		return (false);
878 	}
879 
880 	/* AT+CMGF is mode selection for message format  */
881 	if (strncasecmp(*buf, "MGF", 3) == 0) {
882 		buf[0] += 3;
883 		switch (**buf) {
884 		case '=':
885 			buf[0]++;
886 			switch (**buf) {
887 			case '0':
888 				buf[0]++;
889 				MessageFormat = PDU_MODE;
890 				break;
891 			case '1':
892 				buf[0]++;
893 				MessageFormat = TEXT_MODE;
894 				break;
895 			default:
896 				return (true);
897 			}
898 			break;
899 		case '?':
900 			buf[0]++;
901 			gsprintf(buffer, MAX_LINE_LENGTH, "+CMGF: %d\r\n", MessageFormat);
902 			gn_atem_string_out(buffer);
903 			break;
904 		default:
905 			return (true);
906 		}
907 		return (false);
908 	}
909 
910 	/* AT+CMGR is reading a message */
911 	if (strncasecmp(*buf, "MGR", 3) == 0) {
912 		buf[0] += 3;
913 		switch (**buf) {
914 		case '=':
915 			buf[0]++;
916 			index = atoi(*buf);
917 			buf[0] += strlen(*buf);
918 
919 			data.sms->memory_type = SMSType;
920 			data.sms->number = index;
921 			error = gn_sms_get(&data, sm);
922 
923 			switch (error) {
924 			case GN_ERR_NONE:
925 				gn_atem_sms_print(buffer2, data.sms, MessageFormat);
926 				gsprintf(buffer, MAX_LINE_LENGTH, "+CMGR: %s\r\n", buffer2);
927 				gn_atem_string_out(buffer);
928 				break;
929 			default:
930 				gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error);
931 				gn_atem_string_out(buffer);
932 				return (true);
933 			}
934 			break;
935 		default:
936 			return (true);
937 		}
938 		return (false);
939 	}
940 
941 	/* AT+CMGS is sending a message */
942 	if (strncasecmp(*buf, "MGS", 3) == 0) {
943 		buf[0] += 3;
944 		switch (**buf) {
945 		case '=':
946 			buf[0]++;
947 			if (sscanf(*buf, "\"%[+0-9a-zA-Z ]\"", sms.remote.number)) {
948 				Parser = gn_atem_sms_parseText;
949 				buf[0] += strlen(*buf);
950 				gn_atem_string_out("\r\n> ");
951 			}
952 			return (true);
953 		default:
954 			return (true);
955 		}
956 		return (false);
957 	}
958 
959 	/* AT+CMGL is listing messages */
960 	if (strncasecmp(*buf, "MGL", 3) == 0) {
961 		buf[0] += 3;
962 		status = -1;
963 
964 		switch (**buf) {
965 		case 0:
966 		case '=':
967 			buf[0]++;
968 			/* process <stat> parameter */
969 			if (*(*buf-1) == 0 || /* i.e. no parameter given */
970 				strcasecmp(*buf, "1") == 0 ||
971 				strcasecmp(*buf, "3") == 0 ||
972 				strcasecmp(*buf, "\"REC READ\"") == 0 ||
973 				strcasecmp(*buf, "\"STO SENT\"") == 0) {
974 				status = GN_SMS_Sent;
975 			} else if (strcasecmp(*buf, "0") == 0 ||
976 				strcasecmp(*buf, "2") == 0 ||
977 				strcasecmp(*buf, "\"REC UNREAD\"") == 0 ||
978 				strcasecmp(*buf, "\"STO UNSENT\"") == 0) {
979 				status = GN_SMS_Unsent;
980 			} else if (strcasecmp(*buf, "4") == 0 ||
981 				strcasecmp(*buf, "\"ALL\"") == 0) {
982 				status = 4; /* ALL */
983 			} else {
984 				return true;
985 			}
986 			buf[0] += strlen(*buf);
987 
988 			/* check all message storages */
989 			for (index = 1; index <= 20; index++) {
990 
991 				data.sms->memory_type = SMSType;
992 				data.sms->number = index;
993 				error = gn_sms_get(&data, sm);
994 
995 				switch (error) {
996 				case GN_ERR_NONE:
997 					/* print messsage if it has the required status */
998 					if (data.sms->status == status || status == 4 /* ALL */) {
999 						gn_atem_sms_print(buffer2, data.sms, MessageFormat);
1000 						gsprintf(buffer, MAX_LINE_LENGTH, "+CMGL: %d,%s\r\n", index, buffer2);
1001 						gn_atem_string_out(buffer);
1002 					}
1003 					break;
1004 				case GN_ERR_EMPTYLOCATION:
1005 					/* don't care if this storage is empty */
1006 					break;
1007 				default:
1008 					/* print other error codes and quit */
1009 					gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error);
1010 					gn_atem_string_out(buffer);
1011 					return (true);
1012 				}
1013 			}
1014 			break;
1015 		default:
1016 			return (true);
1017 		}
1018 		return (false);
1019 	}
1020 
1021 	return (true);
1022 }
1023 
1024 /* AT+G commands.  Some of these responses are a bit tongue in cheek... */
gn_atem_command_plusg(char ** buf)1025 bool	gn_atem_command_plusg(char **buf)
1026 {
1027 	char		buffer[MAX_LINE_LENGTH];
1028 
1029 	/* AT+GMI is Manufacturer information for the TA (Terminal Adaptor) */
1030 	if (strncasecmp(*buf, "MI", 3) == 0) {
1031 		buf[0] += 2;
1032 
1033 		gn_atem_string_out(_("Hugh Blemings, Pavel Janik ml. and others...\r\n"));
1034 		return (false);
1035 	}
1036 
1037 	/* AT+GMR is Revision information for the TA (Terminal Adaptor) */
1038 	if (strncasecmp(*buf, "MR", 3) == 0) {
1039 		buf[0] += 2;
1040 		gsprintf(buffer, MAX_LINE_LENGTH, "%s %s %s\r\n", VERSION, __TIME__, __DATE__);
1041 
1042 		gn_atem_string_out(buffer);
1043 		return (false);
1044 	}
1045 
1046 	/* AT+GMM is Model information for the TA (Terminal Adaptor) */
1047 	if (strncasecmp(*buf, "MM", 3) == 0) {
1048 		buf[0] += 2;
1049 
1050 		gsprintf(buffer, MAX_LINE_LENGTH, _("gnokii configured on %s for models %s\r\n"), sm->config.port_device, sm->driver.phone.models);
1051 		gn_atem_string_out(buffer);
1052 		return (false);
1053 	}
1054 
1055 	/* AT+GSN is Serial number for the TA (Terminal Adaptor) */
1056 	if (strncasecmp(*buf, "SN", 3) == 0) {
1057 		buf[0] += 2;
1058 
1059 		gsprintf(buffer, MAX_LINE_LENGTH, _("none built in, choose your own\r\n"));
1060 		gn_atem_string_out(buffer);
1061 		return (false);
1062 	}
1063 
1064 	return (true);
1065 }
1066 
1067 /* Handle AT# commands */
gn_atem_command_diesis(char ** buf)1068 bool	gn_atem_command_diesis(char **buf)
1069 {
1070 	int	number;
1071 	char	buffer[MAX_LINE_LENGTH];
1072 
1073 	if (strncasecmp(*buf, "CID", 3) == 0) {
1074 		buf[0] += 3;
1075 		switch (**buf) {
1076 		case '?':
1077 			buf[0]++;
1078 			gsprintf(buffer, MAX_LINE_LENGTH, "%d\r\n", CallerIDMode);
1079 			gn_atem_string_out(buffer);
1080 			return (false);
1081 		case '=':
1082 			buf[0]++;
1083 			if (**buf == '?') {
1084 				buf[0]++;
1085 				gn_atem_string_out("0,1\r\n");
1086 				return (false);
1087 			} else {
1088 				number = gn_atem_num_get(buf);
1089 				if ( number == 0 || number == 1 ) {
1090 					CallerIDMode = number;
1091 					return (false);
1092 				}
1093 			}
1094 		}
1095 	}
1096 	return (true);
1097 }
1098 
1099 /* Send a result string back.  There is much work to do here, see
1100    the code in the isdn driver for an idea of where it's heading... */
gn_atem_modem_result(int code)1101 void	gn_atem_modem_result(int code)
1102 {
1103 	char	buffer[16];
1104 
1105 	if (!(ModemRegisters[REG_VERBOSE] & BIT_VERBOSE)) {
1106 		snprintf(buffer, sizeof(buffer), "%d\r\n", code);
1107 		gn_atem_string_out(buffer);
1108 	} else {
1109 		switch (code) {
1110 			case MR_OK:
1111 					gn_atem_string_out("OK\r\n");
1112 					break;
1113 
1114 			case MR_ERROR:
1115 					gn_atem_string_out("ERROR\r\n");
1116 					break;
1117 
1118 			case MR_CARRIER:
1119 					gn_atem_string_out("CARRIER\r\n");
1120 					break;
1121 
1122 			case MR_CONNECT:
1123 					gn_atem_string_out("CONNECT\r\n");
1124 					break;
1125 
1126 			case MR_NOCARRIER:
1127 					gn_atem_string_out("NO CARRIER\r\n");
1128 					break;
1129 		        case MR_RING:
1130 					gn_atem_string_out("RING\r\n");
1131 					break;
1132 			default:
1133 					gn_atem_string_out(_("\r\nUnknown Result Code!\r\n"));
1134 					break;
1135 		}
1136 	}
1137 
1138 }
1139 
1140 
1141 /* Get integer from char-pointer, set pointer to end of number
1142    stolen basically verbatim from ISDN code.  */
gn_atem_num_get(char ** p)1143 int gn_atem_num_get(char **p)
1144 {
1145 	int v = -1;
1146 
1147 	while (*p[0] >= '0' && *p[0] <= '9') {
1148 		v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
1149 	}
1150 
1151 	return v;
1152 }
1153 
1154 /* Write string to virtual modem port, either pty or
1155    STDOUT as appropriate.  This function is only used during
1156    command mode - data pump is used when connected.  */
gn_atem_string_out(char * buffer)1157 void	gn_atem_string_out(char *buffer)
1158 {
1159 	int	count = 0;
1160 	char	out_char;
1161 
1162 	while (count < strlen(buffer)) {
1163 
1164 		/* Translate CR/LF/BS as appropriate */
1165 		switch (buffer[count]) {
1166 			case '\r':
1167 				out_char = ModemRegisters[REG_CR];
1168 				break;
1169 			case '\n':
1170 				out_char = ModemRegisters[REG_LF];
1171 				break;
1172 			case '\b':
1173 				out_char = ModemRegisters[REG_BS];
1174 				break;
1175 			default:
1176 				out_char = buffer[count];
1177 				break;
1178 		}
1179 
1180 		if (write(PtyWRFD, &out_char, 1) < 0) {
1181 			fprintf(stderr, _("Failed to output string.\n"));
1182 			perror("write");
1183 			return;
1184 		}
1185 		count++;
1186 	}
1187 }
1188