📄 at-emulator.c
字号:
/* $Id: at-emulator.c,v 1.38 2003/12/16 00:56:27 bozo Exp $ G N O K I I A Linux/Unix toolset and driver for Nokia mobile phones. This file is part of gnokii. Gnokii is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Gnokii is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with gnokii; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Copyright (C) 1999, 2000 Hugh Blemings & Pavel Jan韐 ml. This file provides a virtual modem or "AT" interface to the GSM phone by calling code in gsm-api.c. Inspired by and in places copied from the Linux kernel AT Emulator IDSN code by Fritz Elfert and others.*/#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <grp.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <ctype.h>#ifndef WIN32# include <termios.h>#endif#include "config.h"#include "misc.h"#include "gnokii.h"#include "data/at-emulator.h"#include "data/datapump.h"#define MAX_LINE_LENGTH 256#define MAX_CMD_BUFFERS (2)#define CMD_BUFFER_LENGTH (100)/* Definition of some special Registers of AT-Emulator, pinched in part from ISDN driver in Linux kernel */#define REG_RINGATA 0#define REG_RINGCNT 1#define REG_ESC 2#define REG_CR 3#define REG_LF 4#define REG_BS 5#define S22 22#define S35 35#define REG_CTRLZ 100#define REG_ESCAPE 101#define REG_QUIET 14#define BIT_QUIET 4#define REG_VERBOSE 14#define BIT_VERBOSE 8#define REG_ECHO 14#define BIT_ECHO 2#define MAX_MODEM_REGISTERS 102/* Message format definitions */#define PDU_MODE 0#define TEXT_MODE 1#define INTERACT_MODE 2/* Global variables */bool gn_atem_initialised = false; /* Set to true once initialised */extern bool CommandMode;extern int ConnectCount;struct gn_statemachine *sm;gn_data data;static gn_sms sms;static gn_call_info callinfo;static char imei[64], model[64], revision[64], manufacturer[64];/* Local variables */static int PtyRDFD; /* File descriptor for reading and writing to/from */static int PtyWRFD; /* pty interface - only different in debug mode. */static u8 ModemRegisters[MAX_MODEM_REGISTERS];static char CmdBuffer[MAX_CMD_BUFFERS][CMD_BUFFER_LENGTH];static int CurrentCmdBuffer;static int CurrentCmdBufferIndex;static int IncomingCallNo;static int MessageFormat; /* Message Format (text or pdu) */ /* Current command parser */static void (*Parser)(char *); /* Current command parser *//* void (*Parser)(char *) = gn_atem_at_parse; */static gn_memory_type SMSType;static int SMSNumber;/* If initialised in debug mode, stdin/out is used instead of ptys for interface. */bool gn_atem_initialise(int read_fd, int write_fd, struct gn_statemachine *vmsm){ PtyRDFD = read_fd; PtyWRFD = write_fd; gn_data_clear(&data); memset(&sms, 0, sizeof(sms)); memset(&callinfo, 0, sizeof(callinfo)); data.sms = &sms; data.call_info = &callinfo; data.manufacturer = manufacturer; data.model = model; data.revision = revision; data.imei = imei; sm = vmsm; /* Initialise command buffer variables */ CurrentCmdBuffer = 0; CurrentCmdBufferIndex = 0; /* Initialise registers */ gn_atem_registers_init(); /* Initial parser is AT routine */ Parser = gn_atem_at_parse; /* Setup defaults for AT*C interpreter. */ SMSNumber = 1; SMSType = GN_MT_ME; /* Default message format is PDU */ MessageFormat = PDU_MODE; /* Set the call passup so that we get notified of incoming calls */ data.call_notification = gn_atem_call_passup; gn_sm_functions(GN_OP_SetCallNotification, &data, sm); /* query model, revision and imei */ if (gn_sm_functions(GN_OP_Identify, &data, sm) != GN_ERR_NONE) return false; /* We're ready to roll... */ gn_atem_initialised = true; return (true);}/* Initialise the "registers" used by the virtual modem. */void gn_atem_registers_init(void){ memset(ModemRegisters, 0, sizeof(ModemRegisters)); ModemRegisters[REG_RINGATA] = 0; ModemRegisters[REG_RINGCNT] = 0; ModemRegisters[REG_ESC] = '+'; ModemRegisters[REG_CR] = 10; ModemRegisters[REG_LF] = 13; ModemRegisters[REG_BS] = 8; ModemRegisters[S35]=7; ModemRegisters[REG_ECHO] |= BIT_ECHO; ModemRegisters[REG_VERBOSE] |= BIT_VERBOSE; ModemRegisters[REG_CTRLZ] = 26; ModemRegisters[REG_ESCAPE] = 27;}static void gn_atem_hangup_phone(void){ if (IncomingCallNo > 0) { rlp_user_request_set(Disc_Req, true); gn_sm_loop(10, sm); } if (IncomingCallNo > 0) { data.call_info->call_id = IncomingCallNo; gn_sm_functions(GN_OP_CancelCall, &data, sm); IncomingCallNo = -1; } dp_Initialise(PtyRDFD, PtyWRFD);}static void gn_atem_answer_phone(void){ /* For now we'll also initialise the datapump + rlp code again */ dp_Initialise(PtyRDFD, PtyWRFD); data.call_notification = dp_CallPassup; gn_sm_functions(GN_OP_SetCallNotification, &data, sm); data.call_info->call_id = IncomingCallNo; gn_sm_functions(GN_OP_AnswerCall, &data, sm); CommandMode = false;}/* This gets called to indicate an incoming call */void gn_atem_call_passup(gn_call_status CallStatus, gn_call_info *CallInfo, struct gn_statemachine *state){ dprintf("gn_atem_call_passup called with %d\n", CallStatus); switch (CallStatus) { case GN_CALL_Incoming: gn_atem_modem_result(MR_RING); IncomingCallNo = CallInfo->call_id; ModemRegisters[REG_RINGCNT]++; if (ModemRegisters[REG_RINGATA] != 0) gn_atem_answer_phone(); break; case GN_CALL_LocalHangup: case GN_CALL_RemoteHangup: IncomingCallNo = -1; break; default: break; }}/* Handler called when characters received from serial port. calls state machine code to process it. */void gn_atem_incoming_data_handle(char *buffer, int length){ int count; unsigned char out_buf[3]; for (count = 0; count < length ; count++) { /* If it's a command terminator character, parse what we have so far then go to next buffer. */ if (buffer[count] == ModemRegisters[REG_CR] || buffer[count] == ModemRegisters[REG_LF] || buffer[count] == ModemRegisters[REG_CTRLZ] || buffer[count] == ModemRegisters[REG_ESCAPE]) { /* Echo character if appropriate. */ if (buffer[count] == ModemRegisters[REG_LF] && (ModemRegisters[REG_ECHO] & BIT_ECHO)) { gn_atem_string_out("\r\n"); } /* Save CTRL-Z and ESCAPE for the parser */ if (buffer[count] == ModemRegisters[REG_CTRLZ] || buffer[count] == ModemRegisters[REG_ESCAPE]) CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex++] = buffer[count]; CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex] = 0x00; Parser(CmdBuffer[CurrentCmdBuffer]); CurrentCmdBuffer++; if (CurrentCmdBuffer >= MAX_CMD_BUFFERS) { CurrentCmdBuffer = 0; } CurrentCmdBufferIndex = 0; } else if (buffer[count] == ModemRegisters[REG_BS]) { if (CurrentCmdBufferIndex > 0) { /* Echo character if appropriate. */ if (ModemRegisters[REG_ECHO] & BIT_ECHO) { gn_atem_string_out("\b \b"); } CurrentCmdBufferIndex--; } } else { /* Echo character if appropriate. */ if (ModemRegisters[REG_ECHO] & BIT_ECHO) { out_buf[0] = buffer[count]; out_buf[1] = 0; gn_atem_string_out(out_buf); } /* Collect it to command buffer */ CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex++] = buffer[count]; if (CurrentCmdBufferIndex >= CMD_BUFFER_LENGTH) { CurrentCmdBufferIndex = CMD_BUFFER_LENGTH; } } }}/* Parser for standard AT commands. cmd_buffer must be null terminated. */void gn_atem_at_parse(char *cmd_buffer){ char *buf; int regno, val; char str[256]; if (strncasecmp (cmd_buffer, "AT", 2) != 0) { gn_atem_modem_result(MR_ERROR); return; } for (buf = &cmd_buffer[2]; *buf;) { switch (toupper(*buf)) { case 'Z': /* Reset modem */ buf++; switch (gn_atem_num_get(&buf)) { case -1: case 0: /* reset and load stored profile 0 */ case 1: /* reset and load stored profile 1 */ gn_atem_hangup_phone(); gn_atem_registers_init(); break; default: gn_atem_modem_result(MR_ERROR); return; } break; case 'A': /* Answer call */ buf++; gn_atem_answer_phone(); return; break; case 'D': /* Dial Data :-) */ /* FIXME - should parse this better */ /* For now we'll also initialise the datapump + rlp code again */ dp_Initialise(PtyRDFD, PtyWRFD); buf++; if (toupper(*buf) == 'T' || toupper(*buf) == 'P') buf++; while (*buf == ' ') buf++; data.call_notification = dp_CallPassup; gn_sm_functions(GN_OP_SetCallNotification, &data, sm); snprintf(data.call_info->number, sizeof(data.call_info->number), "%s", buf); if (ModemRegisters[S35] == 0) data.call_info->type = GN_CALL_DigitalData; else data.call_info->type = GN_CALL_NonDigitalData; data.call_info->send_number = GN_CALL_Default; CommandMode = false; if (gn_sm_functions(GN_OP_MakeCall, &data, sm) != GN_ERR_NONE) { CommandMode = true; dp_CallPassup(GN_CALL_RemoteHangup, NULL, NULL); } else { IncomingCallNo = data.call_info->call_id; gn_sm_loop(10, sm); } return; break; case 'H': /* Hang Up */ buf++; switch (gn_atem_num_get(&buf)) { case -1: case 0: /* hook off the phone */ gn_atem_hangup_phone(); break; case 1: /* hook on the phone */ break; default: gn_atem_modem_result(MR_ERROR); return; } break; case 'S': /* Change registers */ buf++; regno = gn_atem_num_get(&buf); if (regno < 0 || regno >= MAX_MODEM_REGISTERS) { gn_atem_modem_result(MR_ERROR); return; } if (*buf == '=') { buf++; val = gn_atem_num_get(&buf); if (val < 0 || val > 255) { gn_atem_modem_result(MR_ERROR); return; } ModemRegisters[regno] = val; } else if (*buf == '?') { buf++; snprintf(str, sizeof(str), "%d\r\n", ModemRegisters[regno]); gn_atem_string_out(str); } else { gn_atem_modem_result(MR_ERROR); return; } break; case 'E': /* E - Turn Echo on/off */ buf++; switch (gn_atem_num_get(&buf)) { case -1: case 0: ModemRegisters[REG_ECHO] &= ~BIT_ECHO; break; case 1: ModemRegisters[REG_ECHO] |= BIT_ECHO; break; default: gn_atem_modem_result(MR_ERROR); return; } break; case 'Q': /* Q - Turn Quiet on/off */ buf++; switch (gn_atem_num_get(&buf)) { case -1: case 0: ModemRegisters[REG_QUIET] &= ~BIT_QUIET; break; case 1: ModemRegisters[REG_QUIET] |= BIT_QUIET; break; default: gn_atem_modem_result(MR_ERROR); return; } break; case 'V': /* V - Turn Verbose on/off */ buf++; switch (gn_atem_num_get(&buf)) { case -1: case 0: ModemRegisters[REG_VERBOSE] &= ~BIT_VERBOSE; break; case 1: ModemRegisters[REG_VERBOSE] |= BIT_VERBOSE; break; default: gn_atem_modem_result(MR_ERROR); return; } break; case 'X': /* X - Set verbosity of the result messages */ buf++; switch (gn_atem_num_get(&buf)) { case -1: case 0: val = 0x00; break; case 1: val = 0x40; break; case 2: val = 0x50; break; case 3: val = 0x60; break; case 4: val = 0x70; break; case 5: val = 0x10; break; default: gn_atem_modem_result(MR_ERROR); return; } ModemRegisters[S22] = (ModemRegisters[S22] & 0x8f) | val; break; case 'I': /* I - info */ buf++; switch (gn_atem_num_get(&buf)) { case -1: case 0: /* terminal id */ snprintf(str, sizeof(str), "%d\r\n", ModemRegisters[39]); gn_atem_string_out(str); break; case 1: /* serial number (IMEI) */ snprintf(str, sizeof(str), "%s\r\n", imei); gn_atem_string_out(str); break; case 2: /* phone revision */ snprintf(str, sizeof(str), "%s\r\n", revision); gn_atem_string_out(str); break; case 3: /* modem revision */ gn_atem_string_out("gnokiid " VERSION "\r\n"); break; case 4: /* OEM string */ snprintf(str, sizeof(str), "%s %s\r\n", manufacturer, model); gn_atem_string_out(str); break; default: gn_atem_modem_result(MR_ERROR); return; } break; /* Handle AT* commands (Nokia proprietary I think) */ case '*': buf++; if (!strcasecmp(buf, "NOKIATEST")) { gn_atem_modem_result(MR_OK); /* FIXME? */ return; } else { if (!strcasecmp(buf, "C")) { gn_atem_modem_result(MR_OK); Parser = gn_atem_sms_parse; return; } } break; /* + is the precursor to another set of commands */ case '+': buf++; switch (toupper(*buf)) { case 'C': buf++; /* Returns true if error occured */ if (gn_atem_command_plusc(&buf) == true) { return; } break; case 'G': buf++; /* Returns true if error occured */ if (gn_atem_command_plusg(&buf) == true) { return; } break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -