/* Parser for DIR sub mode of SMS interactive mode. */ void gn_atem_dir_parse(char *buff) { switch (toupper(*buff)) { case 'P': SMSNumber--; gn_atem_sms_handle(); return; case 'N': SMSNumber++; gn_atem_sms_handle(); return; case 'D': data.sms->memory_type = SMSType; data.sms->number = SMSNumber; if (gn_sm_functions(GN_OP_DeleteSMS, &data, sm) == GN_ERR_NONE) { gn_atem_modem_result(MR_OK); } else { gn_atem_string_out(gn_atem_cme(21)); gn_atem_string_out("\r\n"); } return; case 'Q': Parser= gn_atem_sms_parse; gn_atem_modem_result(MR_OK); return; } gn_atem_modem_result(MR_ERROR); }
/* Handle AT# commands */ bool gn_atem_command_diesis(char **buf) { int number; char buffer[MAX_LINE_LENGTH]; if (strncasecmp(*buf, "CID", 3) == 0) { buf[0] += 3; switch (**buf) { case '?': buf[0]++; gsprintf(buffer, MAX_LINE_LENGTH, "%d\r\n", CallerIDMode); gn_atem_string_out(buffer); return (false); case '=': buf[0]++; if (**buf == '?') { buf[0]++; gn_atem_string_out("0,1\r\n"); return (false); } else { number = gn_atem_num_get(buf); if ( number == 0 || number == 1 ) { CallerIDMode = number; return (false); } } } } return (true); }
/* 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_CR] && (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((char *)out_buf); } /* Collect it to command buffer */ CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex++] = buffer[count]; if (CurrentCmdBufferIndex >= CMD_BUFFER_LENGTH) { CurrentCmdBufferIndex = CMD_BUFFER_LENGTH; } } } }
/* AT+G commands. Some of these responses are a bit tongue in cheek... */ bool gn_atem_command_plusg(char **buf) { char buffer[MAX_LINE_LENGTH]; /* AT+GMI is Manufacturer information for the TA (Terminal Adaptor) */ if (strncasecmp(*buf, "MI", 3) == 0) { buf[0] += 2; gn_atem_string_out(_("Hugh Blemings, Pavel Janik ml. and others...\r\n")); return (false); } /* AT+GMR is Revision information for the TA (Terminal Adaptor) */ if (strncasecmp(*buf, "MR", 3) == 0) { buf[0] += 2; gsprintf(buffer, MAX_LINE_LENGTH, "%s %s %s\r\n", VERSION, __TIME__, __DATE__); gn_atem_string_out(buffer); return (false); } /* AT+GMM is Model information for the TA (Terminal Adaptor) */ if (strncasecmp(*buf, "MM", 3) == 0) { buf[0] += 2; gsprintf(buffer, MAX_LINE_LENGTH, _("gnokii configured on %s for models %s\r\n"), sm->config.port_device, sm->driver.phone.models); gn_atem_string_out(buffer); return (false); } /* AT+GSN is Serial number for the TA (Terminal Adaptor) */ if (strncasecmp(*buf, "SN", 3) == 0) { buf[0] += 2; gsprintf(buffer, MAX_LINE_LENGTH, _("none built in, choose your own\r\n")); gn_atem_string_out(buffer); return (false); } /* AT+GCAP is overall capabilities of TA */ if (strncasecmp(*buf, "CAP", 4) == 0) { buf[0] += 3; gsprintf(buffer, MAX_LINE_LENGTH, "+GCAP:+CGSM,+FCLASS\r\n"); gn_atem_string_out(buffer); return (false); } return (true); }
/* Parser for entering message content (+CMGS) */ void gn_atem_sms_parseText(char *buff) { static int index = 0; int i, length; char buffer[MAX_LINE_LENGTH]; gn_error error; length = strlen(buff); sms.user_data[0].type = GN_SMS_DATA_Text; for (i = 0; i < length; i++) { if (buff[i] == ModemRegisters[REG_CTRLZ]) { /* Exit SMS text mode with sending */ sms.user_data[0].u.text[index] = 0; sms.user_data[0].length = index; index = 0; Parser = gn_atem_at_parse; dprintf("Sending SMS to %s (text: %s)\n", data.sms->remote.number, data.sms->user_data[0].u.text); /* FIXME: set more SMS fields before sending */ error = gn_sms_send(&data, sm); if (error == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "+CMGS: %d\r\n", data.sms->number); gn_atem_string_out(buffer); gn_atem_modem_result(MR_OK); } else { gn_atem_string_out(gn_atem_cme(0)); gn_atem_string_out("\r\n"); } return; } else if (buff[i] == ModemRegisters[REG_ESCAPE]) { /* Exit SMS text mode without sending */ sms.user_data[0].u.text[index] = 0; sms.user_data[0].length = index; index = 0; Parser = gn_atem_at_parse; gn_atem_modem_result(MR_OK); return; } else { /* Appent next char to message text */ sms.user_data[0].u.text[index++] = buff[i]; } } /* reached the end of line so insert \n and wait for more */ sms.user_data[0].u.text[index++] = '\n'; gn_atem_string_out("\r\n> "); }
/* This gets called to output caller id info of incoming call */ void gn_atem_cid_out(gn_call_info *CallInfo) { struct tm *now; time_t nowh; char buf[14]; /* 7 for "DATE = " + 4 digits + \n + \r + \0 */ nowh = time(NULL); now = localtime(&nowh); switch (CallerIDMode) { case 0: /* no output */ break; case 1: /* formatted CID */ snprintf(buf, sizeof(buf), "DATE = %02d%02d\r\n", now->tm_mon + 1, now->tm_mday); gn_atem_string_out(buf); snprintf(buf, sizeof(buf), "TIME = %02d%02d\r\n", now->tm_hour, now->tm_min); gn_atem_string_out(buf); /* TO DO: handle P and O numbers */ gn_atem_string_out("NMBR = "); gn_atem_string_out(1 + CallInfo->number); /* skip leading "+" */ gn_atem_string_out("\r\nNAME = "); gn_atem_string_out(CallInfo->name); gn_atem_string_out("\r\n"); /* FIX ME: do a real emulation of rings after the first one (at a lower level than this) */ gn_atem_modem_result(MR_RING); break; } }
/* Handle AT% commands, this is a quick hack together at this stage. */ bool gn_atem_command_percent(char **buf) { char buffer[MAX_LINE_LENGTH]; /* This command is undocumented. */ if (!strncasecmp(*buf, "CSQ", 3)) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; switch (**buf) { case '0': buf[0]++; data.csq = 0; break; case '1': buf[0]++; data.csq = 1; data.signal_quality_notification = gn_atem_signal_quality; break; case '?': break; default: return (true); } gsprintf(buffer, MAX_LINE_LENGTH, "%%CSQ: %i\r\n", data.csq); break; case '?': buf[0]++; gsprintf(buffer, MAX_LINE_LENGTH, "%%CSQ: (0-31,99),(0-7,99)\r\n"); gn_atem_string_out(buffer); break; default: return (true); } return (false); } /* AT%BAND is Frequency Band Information */ if (!strncasecmp(*buf, "BAND", 4)) { buf[0] += 4; return gn_atem_parse_option(buf, &gn_atem_op_band, data.band); } /* AT%CPI is Call Progress Information */ if (!strncasecmp(*buf, "CPI", 3)) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cpi, data.cpi); } return (true); }
static void gn_atem_sms_handle() { gn_error error; char buffer[MAX_LINE_LENGTH]; data.sms->memory_type = SMSType; data.sms->number = SMSNumber; error = gn_sms_get(&data, sm); switch (error) { case GN_ERR_NONE: gn_atem_sms_print(buffer, data.sms, INTERACT_MODE); gn_atem_string_out(buffer); break; default: gsprintf(buffer, MAX_LINE_LENGTH, _("\r\nNo message under number %d\r\n"), SMSNumber); gn_atem_string_out(buffer); break; } return; }
/* Parser for SMS interactive mode */ void gn_atem_sms_parse(char *buff) { if (!strcasecmp(buff, "HELP")) { gn_atem_string_out(_("\r\nThe following commands work...\r\n")); gn_atem_string_out("DIR\r\n"); gn_atem_string_out("EXIT\r\n"); gn_atem_string_out("HELP\r\n"); return; } if (!strcasecmp(buff, "DIR")) { SMSNumber = 1; gn_atem_sms_handle(); Parser = gn_atem_dir_parse; return; } if (!strcasecmp(buff, "EXIT")) { Parser = gn_atem_at_parse; gn_atem_modem_result(MR_OK); return; } gn_atem_modem_result(MR_ERROR); }
void gn_atem_cpi(enum gsmd_call_progress msg, enum gsmd_call_direction dir, int inband) { char *buffer; if (data.cpi[0] < '1') return; /* Format: %CPI: <cId>,<msgType>,<ibt>,<tch>,<dir>,[<mode>], * [<number>],[<type>],[<alpha>],[<cause>],<line> */ asprintf(&buffer, "%%CPI: %i,%i,0,%i,%i,0,,,,,0\r\n", IncomingCallNo, msg, inband, dir); gn_atem_string_out(buffer); free(buffer); }
static void gn_atem_signal_quality(struct gn_statemachine *state) { float rflevel = -1; gn_rf_unit rfunits = GN_RF_CSQ; char buffer[MAX_LINE_LENGTH]; if (!data.csq) return; data.rf_unit = &rfunits; data.rf_level = &rflevel; if (gn_sm_functions(GN_OP_GetRFLevel, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "%%CSQ: %.f, 99, 2\r\n", *(data.rf_level)); gn_atem_string_out(buffer); } }
/* Send a result string back. There is much work to do here, see the code in the isdn driver for an idea of where it's heading... */ void gn_atem_modem_result(int code) { char buffer[16]; if (!(ModemRegisters[REG_VERBOSE] & BIT_VERBOSE)) { snprintf(buffer, sizeof(buffer), "%d\r\n", code); gn_atem_string_out(buffer); } else { switch (code) { case MR_OK: gn_atem_string_out("OK\r\n"); break; case MR_ERROR: gn_atem_string_out("ERROR\r\n"); break; case MR_CARRIER: gn_atem_string_out("CARRIER\r\n"); break; case MR_CONNECT: gn_atem_string_out("CONNECT\r\n"); break; case MR_NOCARRIER: gn_atem_string_out("NO CARRIER\r\n"); break; case MR_RING: gn_atem_string_out("RING\r\n"); break; default: gn_atem_string_out(_("\r\nUnknown Result Code!\r\n")); break; } } }
static void gn_atem_network_msg(int connected, struct gn_statemachine *state) { char *buffer; data.connected = connected; /* TODO: Send +COPS and +CREG separately */ /* TODO: Take COPS format and CREG format settings into account */ if (connected) { strcpy(data.cops, "1,\"012C\",\"0DCC\""); } else { strcpy(data.cops, "2"); } if (strcmp(data.creg, "0")) asprintf(&buffer, "+CREG: %s\r\n", data.cops); else /* +CREG: is disabled, but +COPS is allowed */ asprintf(&buffer, "+COPS: %s\r\n", data.cops); gn_atem_string_out(buffer); free(buffer); }
/* Handle AT+C commands, this is a quick hack together at this stage. */ bool gn_atem_command_plusc(char **buf) { float rflevel = -1; gn_rf_unit rfunits = GN_RF_CSQ; char buffer[MAX_LINE_LENGTH], buffer2[MAX_LINE_LENGTH]; int status, index; gn_error error; if (strncasecmp(*buf, "SQ", 2) == 0) { buf[0] += 2; data.rf_unit = &rfunits; data.rf_level = &rflevel; if (gn_sm_functions(GN_OP_GetRFLevel, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "+CSQ: %0.0f, 99\r\n", *(data.rf_level)); gn_atem_string_out(buffer); return (false); } else { return (true); } } /* AT+CGMI is Manufacturer information for the ME (phone) so it should be Nokia rather than gnokii... */ if (strncasecmp(*buf, "GMI", 3) == 0) { buf[0] += 3; gn_atem_string_out(_("Nokia Mobile Phones\r\n")); return (false); } /* AT+CGSN is IMEI */ if (strncasecmp(*buf, "GSN", 3) == 0) { buf[0] += 3; snprintf(data.imei, GN_IMEI_MAX_LENGTH, "+CME ERROR: 0"); if (gn_sm_functions(GN_OP_GetImei, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.imei); gn_atem_string_out(buffer); return (false); } else { return (true); } } /* AT+CGMR is Revision (hardware) */ if (strncasecmp(*buf, "GMR", 3) == 0) { buf[0] += 3; snprintf(data.revision, GN_REVISION_MAX_LENGTH, "+CME ERROR: 0"); if (gn_sm_functions(GN_OP_GetRevision, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.revision); gn_atem_string_out(buffer); return (false); } else { return (true); } } /* AT+CGMM is Model code */ if (strncasecmp(*buf, "GMM", 3) == 0) { buf[0] += 3; snprintf(data.model, GN_MODEL_MAX_LENGTH, "+CME ERROR: 0"); if (gn_sm_functions(GN_OP_GetModel, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.model); gn_atem_string_out(buffer); return (false); } else { return (true); } } /* AT+CMGD is deleting a message */ if (strncasecmp(*buf, "MGD", 3) == 0) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; index = atoi(*buf); buf[0] += strlen(*buf); data.sms->memory_type = SMSType; data.sms->number = index; error = gn_sm_functions(GN_OP_DeleteSMS, &data, sm); switch (error) { case GN_ERR_NONE: break; default: gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error); gn_atem_string_out(buffer); return (true); } break; default: return (true); } return (false); } /* AT+CMGF is mode selection for message format */ if (strncasecmp(*buf, "MGF", 3) == 0) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; switch (**buf) { case '0': buf[0]++; MessageFormat = PDU_MODE; break; case '1': buf[0]++; MessageFormat = TEXT_MODE; break; default: return (true); } break; case '?': buf[0]++; gsprintf(buffer, MAX_LINE_LENGTH, "+CMGF: %d\r\n", MessageFormat); gn_atem_string_out(buffer); break; default: return (true); } return (false); } /* AT+CMGR is reading a message */ if (strncasecmp(*buf, "MGR", 3) == 0) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; index = atoi(*buf); buf[0] += strlen(*buf); data.sms->memory_type = SMSType; data.sms->number = index; error = gn_sms_get(&data, sm); switch (error) { case GN_ERR_NONE: gn_atem_sms_print(buffer2, data.sms, MessageFormat); gsprintf(buffer, MAX_LINE_LENGTH, "+CMGR: %s\r\n", buffer2); gn_atem_string_out(buffer); break; default: gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error); gn_atem_string_out(buffer); return (true); } break; default: return (true); } return (false); } /* AT+CMGS is sending a message */ if (strncasecmp(*buf, "MGS", 3) == 0) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; if (sscanf(*buf, "\"%[+0-9a-zA-Z ]\"", sms.remote.number)) { Parser = gn_atem_sms_parseText; buf[0] += strlen(*buf); gn_atem_string_out("\r\n> "); } return (true); default: return (true); } return (false); } /* AT+CMGL is listing messages */ if (strncasecmp(*buf, "MGL", 3) == 0) { buf[0] += 3; status = -1; switch (**buf) { case 0: case '=': buf[0]++; /* process <stat> parameter */ if (*(*buf-1) == 0 || /* i.e. no parameter given */ strcasecmp(*buf, "1") == 0 || strcasecmp(*buf, "3") == 0 || strcasecmp(*buf, "\"REC READ\"") == 0 || strcasecmp(*buf, "\"STO SENT\"") == 0) { status = GN_SMS_Sent; } else if (strcasecmp(*buf, "0") == 0 || strcasecmp(*buf, "2") == 0 || strcasecmp(*buf, "\"REC UNREAD\"") == 0 || strcasecmp(*buf, "\"STO UNSENT\"") == 0) { status = GN_SMS_Unsent; } else if (strcasecmp(*buf, "4") == 0 || strcasecmp(*buf, "\"ALL\"") == 0) { status = 4; /* ALL */ } else { return true; } buf[0] += strlen(*buf); /* check all message storages */ for (index = 1; index <= 20; index++) { data.sms->memory_type = SMSType; data.sms->number = index; error = gn_sms_get(&data, sm); switch (error) { case GN_ERR_NONE: /* print messsage if it has the required status */ if (data.sms->status == status || status == 4 /* ALL */) { gn_atem_sms_print(buffer2, data.sms, MessageFormat); gsprintf(buffer, MAX_LINE_LENGTH, "+CMGL: %d,%s\r\n", index, buffer2); gn_atem_string_out(buffer); } break; case GN_ERR_EMPTYLOCATION: /* don't care if this storage is empty */ break; default: /* print other error codes and quit */ gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error); gn_atem_string_out(buffer); return (true); } } break; default: return (true); } return (false); } return (true); }
/* Handle AT+C commands, this is a quick hack together at this stage. */ bool gn_atem_command_plusc(char **buf) { float rflevel = -1; gn_rf_unit rfunits = GN_RF_CSQ; char buffer[MAX_LINE_LENGTH], buffer2[MAX_LINE_LENGTH]; int status, index; gn_error error; if (strncasecmp(*buf, "SQ", 2) == 0) { buf[0] += 2; data.rf_unit = &rfunits; data.rf_level = &rflevel; if (gn_sm_functions(GN_OP_GetRFLevel, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "+CSQ: %0.0f, 99\r\n", *(data.rf_level)); gn_atem_string_out(buffer); return (false); } else { return (true); } } /* AT+CGMI is Manufacturer information for the ME (phone) so it should be Nokia rather than gnokii... */ if (strncasecmp(*buf, "GMI", 3) == 0) { buf[0] += 3; gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.manufacturer); gn_atem_string_out(buffer); return (false); } /* AT+CGSN is IMEI */ if (strncasecmp(*buf, "GSN", 3) == 0) { buf[0] += 3; strcpy(data.imei, gn_atem_cme(0)); if (gn_sm_functions(GN_OP_GetImei, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.imei); gn_atem_string_out(buffer); return (false); } else { return (true); } } /* AT+CGMR is Revision (hardware) */ if (strncasecmp(*buf, "GMR", 3) == 0) { buf[0] += 3; strcpy(data.revision, gn_atem_cme(0)); if (gn_sm_functions(GN_OP_GetRevision, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.revision); gn_atem_string_out(buffer); return (false); } else { return (true); } } /* AT+CGMM is Model code */ if (strncasecmp(*buf, "GMM", 3) == 0) { buf[0] += 3; strcpy(data.model, gn_atem_cme(0)); if (gn_sm_functions(GN_OP_GetModel, &data, sm) == GN_ERR_NONE) { gsprintf(buffer, MAX_LINE_LENGTH, "%s\r\n", data.model); gn_atem_string_out(buffer); return (false); } else { return (true); } } /* AT+CMGD is deleting a message */ if (strncasecmp(*buf, "MGD", 3) == 0) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; index = atoi(*buf); buf[0] += strlen(*buf); data.sms->memory_type = SMSType; data.sms->number = index; error = gn_sm_functions(GN_OP_DeleteSMS, &data, sm); switch (error) { case GN_ERR_NONE: break; default: gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error); gn_atem_string_out(buffer); return (true); } break; default: return (true); } return (false); } /* AT+CMGF is mode selection for message format */ if (strncasecmp(*buf, "MGF", 3) == 0) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; switch (**buf) { case '0': buf[0]++; MessageFormat = PDU_MODE; break; case '1': buf[0]++; MessageFormat = TEXT_MODE; break; default: return (true); } break; case '?': buf[0]++; gsprintf(buffer, MAX_LINE_LENGTH, "+CMGF: %d\r\n", MessageFormat); gn_atem_string_out(buffer); break; default: return (true); } return (false); } /* AT+CMGR is reading a message */ if (strncasecmp(*buf, "MGR", 3) == 0) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; index = atoi(*buf); buf[0] += strlen(*buf); data.sms->memory_type = SMSType; data.sms->number = index; error = gn_sms_get(&data, sm); switch (error) { case GN_ERR_NONE: gn_atem_sms_print(buffer2, data.sms, MessageFormat); gsprintf(buffer, MAX_LINE_LENGTH, "+CMGR: %s\r\n", buffer2); gn_atem_string_out(buffer); break; default: gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error); gn_atem_string_out(buffer); return (true); } break; default: return (true); } return (false); } /* AT+CMGS is sending a message */ if (strncasecmp(*buf, "MGS", 3) == 0) { buf[0] += 3; switch (**buf) { case '=': buf[0]++; if (sscanf(*buf, "\"%[+0-9a-zA-Z ]\"", sms.remote.number)) { Parser = gn_atem_sms_parseText; buf[0] += strlen(*buf); gn_atem_string_out("\r\n> "); } return (true); default: return (true); } return (false); } /* AT+CMGL is listing messages */ if (strncasecmp(*buf, "MGL", 3) == 0) { buf[0] += 3; status = -1; switch (**buf) { case 0: case '=': buf[0]++; /* process <stat> parameter */ if (*(*buf-1) == 0 || /* i.e. no parameter given */ strcasecmp(*buf, "1") == 0 || strcasecmp(*buf, "3") == 0 || strcasecmp(*buf, "\"REC READ\"") == 0 || strcasecmp(*buf, "\"STO SENT\"") == 0) { status = GN_SMS_Sent; } else if (strcasecmp(*buf, "0") == 0 || strcasecmp(*buf, "2") == 0 || strcasecmp(*buf, "\"REC UNREAD\"") == 0 || strcasecmp(*buf, "\"STO UNSENT\"") == 0) { status = GN_SMS_Unsent; } else if (strcasecmp(*buf, "4") == 0 || strcasecmp(*buf, "\"ALL\"") == 0) { /* TODO: in TEXT mode only "ALL" should be * accepted, and in PDU mode only the "4" * form. */ status = 4; /* ALL */ } else { return true; } buf[0] += strlen(*buf); /* check all message storages */ for (index = 1; index <= 20; index++) { data.sms->memory_type = SMSType; data.sms->number = index; error = gn_sms_get(&data, sm); switch (error) { case GN_ERR_NONE: /* print messsage if it has the required status */ if (data.sms->status == status || status == 4 /* ALL */) { gn_atem_sms_print(buffer2, data.sms, MessageFormat); gsprintf(buffer, MAX_LINE_LENGTH, "+CMGL: %d,%s\r\n", index, buffer2); gn_atem_string_out(buffer); } break; case GN_ERR_EMPTYLOCATION: /* don't care if this storage is empty */ break; default: /* print other error codes and quit */ gsprintf(buffer, MAX_LINE_LENGTH, "\r\n+CMS ERROR: %d\r\n", error); gn_atem_string_out(buffer); return (true); } } break; default: return (true); } return (false); } /* AT+CSCS is character set selection */ if (strncasecmp(*buf, "SCS", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cscs, data.cscs); } /* AT+CIMI is international mobile subscriber identity */ if (strcasecmp(*buf, "IMI") == 0) { buf[0] += 3; gn_atem_string_out("QEMU_IMSI\r\n"); return (false); } /* AT+CMUX is multiplexing mode */ if (strncasecmp(*buf, "MUX", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cmux, data.cmux); } /* AT+CMEE is Mobile Termination error reporting */ if (strncasecmp(*buf, "MEE", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cmee, data.cmee); } /* AT+CLIP is calling line identification presentation */ if (strncasecmp(*buf, "LIP", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_clip, data.clip); } /* AT+COLP is connected line identification presentation */ if (strncasecmp(*buf, "OLP", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_colp, data.colp); } /* AT+CSTA is address type selection */ if (strncasecmp(*buf, "STA", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_csta, data.csta); } /* AT+CMOD is call mode */ if (strncasecmp(*buf, "MOD", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cmod, data.cmod); } /* AT+CBST is bearer service type */ if (strncasecmp(*buf, "BST", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cbst, data.cbst); } /* AT+CRLP is radio link protocol */ if (strncasecmp(*buf, "RLP", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_crlp, data.crlp); } /* AT+CRC is cellular result codes */ if (strncasecmp(*buf, "RC", 2) == 0) { buf[0] += 2; return gn_atem_parse_option(buf, &gn_atem_op_crc, data.crc); } /* AT+CREG is network registration */ if (strncasecmp(*buf, "REG", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_creg, data.creg); } /* AT+CR is reporting control */ if (strncasecmp(*buf, "R", 1) == 0) { buf[0] += 1; return gn_atem_parse_option(buf, &gn_atem_op_cr, data.cr); } /* AT+CEER is extended error report */ if (strncasecmp(*buf, "EER", 3) == 0) { buf[0] += 3; gn_atem_string_out("+CEER: 0,0,5,16,normal call clearing\r\n"); return (false); } /* AT+CSNS is single numbering scheme */ if (strncasecmp(*buf, "SNS", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_csns, data.csns); } /* AT+COPS is PLMN selection */ if (strncasecmp(*buf, "OPS", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cops, data.cops); } /* AT+CPAS is phone activity status */ if (strncasecmp(*buf, "PAS", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cpas, data.cpas); } /* AT+CFUN is phone functionality */ if (strncasecmp(*buf, "FUN", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_cfun, data.cfun); } /* AT+CTZR is time zone reporting */ if (strncasecmp(*buf, "TZR", 3) == 0) { buf[0] += 3; return gn_atem_parse_option(buf, &gn_atem_op_ctzr, data.ctzr); } /* AT+CBC is battery charge */ if (strncasecmp(*buf, "BC", 2) == 0) { buf[0] += 2; return gn_atem_parse_option(buf, &gn_atem_op_cbc, data.cbc); } /* AT+CSSN is supplementary service notifications */ if (strncasecmp(*buf, "CSSN", 4) == 0) { buf[0] += 4; return gn_atem_parse_option(buf, &gn_atem_op_cssn, data.cssn); } return (true); }
/* 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 (!cmd_buffer[0]) return; 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, 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) { gn_atem_modem_result(MR_ERROR); return; } break; case 'G': buf++; /* Returns true if error occured */ if (gn_atem_command_plusg(&buf) == true) { gn_atem_modem_result(MR_ERROR); return; } break; default: gn_atem_modem_result(MR_ERROR); return; } break; /* # is the precursor to another set of commands */ case '#': buf++; /* Returns true if error occured */ if (gn_atem_command_diesis(&buf) == true) { gn_atem_modem_result(MR_ERROR); return; } break; default: gn_atem_modem_result(MR_ERROR); return; } } gn_atem_modem_result(MR_OK); }
/* Send a result string back. There is much work to do here, see the code in the isdn driver for an idea of where it's heading... */ void gn_atem_modem_result(int code) { char buffer[16]; gn_atem_string_out("\r\n"); /* XXX Some modems do this */ if (!(ModemRegisters[REG_VERBOSE] & BIT_VERBOSE)) { sprintf(buffer, "%d\r\n", code); gn_atem_string_out(buffer); } else { switch (code) { case MR_OK: gn_atem_string_out("OK\r\n"); break; case MR_ERROR: gn_atem_string_out("ERROR\r\n"); break; case MR_CARRIER: gn_atem_string_out("CARRIER\r\n"); break; case MR_CONNECT: gn_atem_string_out("CONNECT\r\n"); break; case MR_NOCARRIER: gn_atem_string_out("NO CARRIER\r\n"); break; case MR_RING: gn_atem_cpi(GSMD_CALLPROG_SETUP, GSMD_CALL_DIR_MT, 0); gn_atem_cpi(GSMD_CALLPROG_SETUP, GSMD_CALL_DIR_MT, 1); if (!strcmp(data.crc, "1")) gn_atem_string_out( "+CRING: VOICE\r\n"); else gn_atem_string_out("RING\r\n"); /* XXX: Standard format is "313373",145 */ if (!strcmp(data.clip, "1,1")) gn_atem_string_out("+CLIP: \"313373\"" ",145,,,,1\r\n"); gn_atem_cpi(GSMD_CALLPROG_SYNC, GSMD_CALL_DIR_MT, 1); break; default: gn_atem_string_out(_("\r\nUnknown Result Code!\r\n")); break; } } }
bool gn_atem_parse_option(char **buf, struct gn_atem_op *op, char *val) { char buffer[MAX_LINE_LENGTH], **strval; int len; if (*val == 0) strcpy(val, op->default_val); if ((*buf)[0] == 0 || ((*buf)[0] == '?' && (*buf)[1] == 0)) { *buf += strlen(*buf); gsprintf(buffer, MAX_LINE_LENGTH, "%s: %s\r\n", op->op, val); gn_atem_string_out(buffer); return (false); } if (*(*buf) ++ != '=') return (true); if (!strcasecmp(*buf, "?")) { (*buf) ++; len = gsprintf(buffer, MAX_LINE_LENGTH, "%s: ", op->op); switch (op->type) { case gn_var_string: strval = op->string_val; len += gsprintf(buffer + len, MAX_LINE_LENGTH - len, "\"%s\"", *strval++); while (*strval) len += gsprintf(buffer + len, MAX_LINE_LENGTH - len, ",\"%s\"", *strval++); break; case gn_var_numbers: strval = op->string_val; len += gsprintf(buffer + len, MAX_LINE_LENGTH - len, "\"%s\"", *strval++); /* TODO */ break; case gn_var_bool: len += gsprintf(buffer + len, MAX_LINE_LENGTH - len, "(0,1)"); break; } gsprintf(buffer + len, MAX_LINE_LENGTH - len, "\r\n"); return (false); } if (!op->writable) return (true); if (op->set_val) return op->set_val(buf, op, val); switch (op->type) { case gn_var_string: for (strval = op->string_val; *strval; strval++) if (!strcasecmp(*buf, *strval)) { gsprintf(val, MAX_LINE_LENGTH, "\"%s\"", *strval); *buf += strlen(*buf); return (false); } break; case gn_var_bool: switch (gn_atem_num_get(buf)) { case 0: strcpy(val, "0"); return (false); case 1: strcpy(val, "1"); return (false); } break; default: break; } return (true); }