r2336 - in trunk/src/host/qemu-neo1973: gnokiigsm hw
andrew at sita.openmoko.org
andrew at sita.openmoko.org
Sat Jun 23 03:01:45 CEST 2007
Author: andrew
Date: 2007-06-23 03:01:43 +0200 (Sat, 23 Jun 2007)
New Revision: 2336
Modified:
trunk/src/host/qemu-neo1973/gnokiigsm/at-emulator.c
trunk/src/host/qemu-neo1973/gnokiigsm/at-emulator.h
trunk/src/host/qemu-neo1973/gnokiigsm/data.h
trunk/src/host/qemu-neo1973/gnokiigsm/statemachine.h
trunk/src/host/qemu-neo1973/hw/modem.c
Log:
Implement many new GSM AT commands (some are undocumented), registering
with network, signal strength, error reporting levels.
Modified: trunk/src/host/qemu-neo1973/gnokiigsm/at-emulator.c
===================================================================
--- trunk/src/host/qemu-neo1973/gnokiigsm/at-emulator.c 2007-06-23 00:58:06 UTC (rev 2335)
+++ trunk/src/host/qemu-neo1973/gnokiigsm/at-emulator.c 2007-06-23 01:01:43 UTC (rev 2336)
@@ -155,14 +155,15 @@
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;
+ if (gn_sm_functions(GN_OP_Identify, &data, sm) != GN_ERR_NONE)
+ return false;
/* We're ready to roll... */
gn_atem_initialised = true;
+ data.connected = 0;
return (true);
}
-
/* Initialise the "registers" used by the virtual modem. */
void gn_atem_registers_init(void)
{
@@ -287,7 +288,7 @@
/* Echo character if appropriate. */
if (buffer[count] == ModemRegisters[REG_CR] &&
(ModemRegisters[REG_ECHO] & BIT_ECHO)) {
- gn_atem_string_out("\r\n");
+ gn_atem_string_out("\r"); /* XXX */
}
/* Save CTRL-Z and ESCAPE for the parser */
@@ -331,6 +332,22 @@
}
}
+static char *gn_atem_cme(int code)
+{
+ static char err[80];
+
+ if (!strcmp(data.cmee, "0"))
+ return "ERROR";
+
+ if (!strcmp(data.cmee, "1"))
+ snprintf(err, sizeof(err), "+CME ERROR: %i", code);
+
+ if (!strcmp(data.cmee, "2"))
+ snprintf(err, sizeof(err), "+CME ERROR: phone error");
+
+ return err;
+}
+
struct gn_atem_op {
char *op;
int writable;
@@ -339,6 +356,7 @@
gn_var_bool, /* 0,1 */
gn_var_numbers, /* (1-5),(9-20) */
} type;
+ bool (*set_val)(char **buf, struct gn_atem_op *op, char *val);
char *default_val;
char *string_val[];
};
@@ -391,6 +409,9 @@
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++)
@@ -440,6 +461,81 @@
},
};
+static bool gn_atem_cmee_set(char **buf, struct gn_atem_op *op, char *val)
+{
+ switch (gn_atem_num_get(buf)) {
+ case 0:
+ strcpy(val, "0");
+ return (false);
+ case 1:
+ strcpy(val, "1");
+ return (false);
+ case 2:
+ strcpy(val, "2");
+ return (false);
+ }
+ return (true);
+}
+
+static struct gn_atem_op gn_atem_op_cmee = {
+ .op = "+CMEE",
+ .writable = 1,
+ .type = gn_var_numbers,
+ .default_val = "0",
+ .string_val = {
+ "(0-2)",
+ },
+ .set_val = gn_atem_cmee_set,
+};
+
+static bool gn_atem_clip_set(char **buf, struct gn_atem_op *op, char *val)
+{
+ switch (gn_atem_num_get(buf)) {
+ case 0:
+ strcpy(val, "0,1"); /* CLIP provisioned in this network */
+ return (false);
+ case 1:
+ strcpy(val, "1,1"); /* CLIP provisioned in this network */
+ return (false);
+ }
+ return (true);
+}
+
+static struct gn_atem_op gn_atem_op_clip = {
+ .op = "+CLIP",
+ .writable = 1,
+ .type = gn_var_numbers,
+ .default_val = "0,1",
+ .string_val = {
+ "(0,1)",
+ },
+ .set_val = gn_atem_clip_set,
+};
+
+static bool gn_atem_colp_set(char **buf, struct gn_atem_op *op, char *val)
+{
+ switch (gn_atem_num_get(buf)) {
+ case 0:
+ strcpy(val, "0,1"); /* COLP provisioned in this network */
+ return (false);
+ case 1:
+ strcpy(val, "1,1"); /* COLP provisioned in this network */
+ return (false);
+ }
+ return (true);
+}
+
+static struct gn_atem_op gn_atem_op_colp = {
+ .op = "+COLP",
+ .writable = 1,
+ .type = gn_var_numbers,
+ .default_val = "0,1",
+ .string_val = {
+ "(0,1)",
+ },
+ .set_val = gn_atem_colp_set,
+};
+
static struct gn_atem_op gn_atem_op_ws46 = {
.op = "+WS46",
.writable = 1,
@@ -517,6 +613,39 @@
},
};
+static bool gn_atem_creg_set(char **buf, struct gn_atem_op *op, char *val)
+{
+ /* <stat> values are:
+ * 0 not registered, MT is not currently searching a
+ * new operator to register to
+ * 1 registered, home network
+ * 2 not registered, but MT is currently searching a
+ * new operator to register to
+ * 3 registration denied
+ * 4 unknown
+ * 5 registered, roaming
+ */
+ switch (gn_atem_num_get(buf)) {
+ case 0:
+ /* No unsolicited +CREG. */
+ strcpy(val, "0,1");
+ return (false);
+ case 1:
+ /* +CREG Syntax is <stat> when there's a change in network
+ * registration status.
+ */
+ strcpy(val, "1,1");
+ return (false);
+ case 2:
+ /* +CREG Syntax is <stat>[,<lac>,<ci>[,<AcT>]] when we've (MT)
+ * succesfully registered in the network cell.
+ */
+ strcpy(val, "2,1");
+ return (false);
+ }
+ return (true);
+}
+
static struct gn_atem_op gn_atem_op_creg = {
.op = "+CREG",
.writable = 1,
@@ -525,13 +654,64 @@
.string_val = {
"(0-2)",
},
+ .set_val = gn_atem_creg_set,
};
+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);
+}
+
+static bool gn_atem_cops_set(char **buf, struct gn_atem_op *op, char *val)
+{
+ data.network_change_notification = gn_atem_network_msg;
+
+ /* Syntax is <mode>[,<format>[,<oper>]] */
+ switch (gn_atem_num_get(buf)) { /* Parse <mode> */
+ case 0:
+ case 1:
+ case 4:
+ if (gn_sm_functions(GN_OP_NetworkRegister, &data, sm) !=
+ GN_ERR_NONE)
+ break;
+ return (false);
+ case 2:
+ if (gn_sm_functions(GN_OP_NetworkUnregister, &data, sm) !=
+ GN_ERR_NONE)
+ break;
+ return (false);
+ case 3: /* Only sets <format>, TODO */
+ return (false);
+ }
+ return (true);
+}
+
static struct gn_atem_op gn_atem_op_cops = {
.op = "+COPS",
.writable = 1,
- .type = gn_var_bool,
- .default_val = "0",
+ .type = gn_var_numbers,
+ .default_val = "2",
+ .string_val = {
+ "(0-4)",
+ },
+ .set_val = gn_atem_cops_set,
};
static struct gn_atem_op gn_atem_op_cpas = {
@@ -544,16 +724,52 @@
},
};
+static bool gn_atem_cfun_set(char **buf, struct gn_atem_op *op, char *val)
+{
+ /* Format is <fun>[,<Rst] */
+ switch (gn_atem_num_get(buf)) { /* Ignore the Reset argument */
+ case 0:
+ /* Minimum functionality */
+ strcpy(val, "0");
+ return (false);
+ case 1:
+ /* Full functionality */
+ strcpy(val, "1");
+ return (false);
+ case 2:
+ /* Disable phone transmit RF circuits only */
+ strcpy(val, "2");
+ return (false);
+ case 3:
+ /* Disable phone receive RF circuits only */
+ strcpy(val, "3");
+ return (false);
+ case 4:
+ /* Disable phone both transmit and receive RF circuits */
+ strcpy(val, "4");
+ return (false);
+ }
+ return (true);
+}
+
static struct gn_atem_op gn_atem_op_cfun = {
.op = "+CFUN",
.writable = 1,
.type = gn_var_numbers,
.default_val = "1",
.string_val = {
- "(0,1,4),(0)",
+ "(0-4),(0)",
},
+ .set_val = gn_atem_cfun_set,
};
+static struct gn_atem_op gn_atem_op_ctzr = {
+ .op = "+CTZR",
+ .writable = 1,
+ .type = gn_var_bool,
+ .default_val = "0",
+};
+
static struct gn_atem_op gn_atem_op_cbc = {
.op = "+CBC",
.writable = 1,
@@ -564,6 +780,24 @@
},
};
+static bool gn_atem_band_set(char **buf, struct gn_atem_op *op, char *val)
+{
+ /* Syntax is <band>[,<mode>] */
+ switch (gn_atem_num_get(buf)) {
+ case 0:
+ /* Automatic */
+ strcpy(val, "0");
+ break;
+ case 1:
+ /* Manual */
+ strcpy(val, "1");
+ break;
+ default:
+ return (true);
+ }
+ return (false);
+}
+
static struct gn_atem_op gn_atem_op_band = {
.op = "%BAND",
.writable = 1,
@@ -572,10 +806,51 @@
.string_val = {
"(0-1),(1-31)",
},
+ .set_val = gn_atem_band_set,
};
+static bool gn_atem_cpi_set(char **buf, struct gn_atem_op *op, char *val)
+{
+ switch (gn_atem_num_get(buf)) {
+ case 0:
+ /* Disable */
+ strcpy(val, "0");
+ break;
+ case 1:
+ /* Enable */
+ strcpy(val, "1");
+ break;
+ case 2:
+ /* Status */
+ strcpy(val, "2");
+ break;
+ case 3:
+ /* Append cause and ALS bearer state to unsolicited results */
+ strcpy(val, "3");
+ break;
+ case 4:
+ /* Append Advance Cause Code */
+ strcpy(val, "4");
+ break;
+ default:
+ return (true);
+ }
+ return (false);
+}
+
+static struct gn_atem_op gn_atem_op_cpi = {
+ .op = "%CPI",
+ .writable = 1,
+ .type = gn_var_numbers,
+ .default_val = "1",
+ .string_val = {
+ "(0-4)",
+ },
+ .set_val = gn_atem_cpi_set,
+};
+
static struct gn_atem_op gn_atem_op_cssn = {
- .op = "CSSN",
+ .op = "+CSSN",
.writable = 1,
.type = gn_var_numbers,
.default_val = "0,0",
@@ -594,8 +869,8 @@
if (!cmd_buffer[0])
return;
- if (strncasecmp (cmd_buffer, "AT", 2) != 0) {
- gn_atem_modem_result(MR_ERROR);
+ if (strncasecmp(cmd_buffer, "AT", 2) != 0) {
+ gn_atem_modem_result(sm->info->non_at_ok ? MR_OK : MR_ERROR);
return;
}
@@ -623,7 +898,6 @@
buf++;
gn_atem_answer_phone();
return;
- break;
case 'D':
/* Dial Data :-) */
@@ -650,7 +924,6 @@
gn_sm_loop(10, sm);
}
return;
- break;
case 'H':
/* Hang Up */
@@ -858,14 +1131,12 @@
/* % is the precursor to another set of commands */
case '%':
buf++;
- if (strncasecmp(buf, "BAND", 3) == 0) {
- buf += 4;
- if (!gn_atem_parse_option(&buf,
- &gn_atem_op_band, data.band))
- break;
+ /* Returns true if error occured */
+ if (gn_atem_command_percent(&buf) == true) {
+ gn_atem_modem_result(MR_ERROR);
+ return;
}
- gn_atem_modem_result(MR_ERROR);
- return;
+ break;
default:
gn_atem_modem_result(MR_ERROR);
@@ -981,7 +1252,8 @@
if (gn_sm_functions(GN_OP_DeleteSMS, &data, sm) == GN_ERR_NONE) {
gn_atem_modem_result(MR_OK);
} else {
- gn_atem_modem_result(MR_ERROR);
+ gn_atem_string_out(gn_atem_cme(21));
+ gn_atem_string_out("\r\n");
}
return;
case 'Q':
@@ -1022,7 +1294,8 @@
gn_atem_string_out(buffer);
gn_atem_modem_result(MR_OK);
} else {
- gn_atem_modem_result(MR_ERROR);
+ gn_atem_string_out(gn_atem_cme(0));
+ gn_atem_string_out("\r\n");
}
return;
} else if (buff[i] == ModemRegisters[REG_ESCAPE]) {
@@ -1060,7 +1333,9 @@
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));
+ gsprintf(buffer, MAX_LINE_LENGTH,
+ "+CSQ: %0.0f, 99\r\n",
+ *(data.rf_level));
gn_atem_string_out(buffer);
return (false);
} else {
@@ -1080,7 +1355,7 @@
/* AT+CGSN is IMEI */
if (strncasecmp(*buf, "GSN", 3) == 0) {
buf[0] += 3;
- strcpy(data.imei, "+CME ERROR: 0");
+ 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);
@@ -1093,7 +1368,7 @@
/* AT+CGMR is Revision (hardware) */
if (strncasecmp(*buf, "GMR", 3) == 0) {
buf[0] += 3;
- strcpy(data.revision, "+CME ERROR: 0");
+ 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);
@@ -1106,7 +1381,7 @@
/* AT+CGMM is Model code */
if (strncasecmp(*buf, "GMM", 3) == 0) {
buf[0] += 3;
- strcpy(data.model, "+CME ERROR: 0");
+ 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);
@@ -1307,6 +1582,24 @@
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;
@@ -1337,6 +1630,12 @@
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;
@@ -1356,12 +1655,6 @@
return gn_atem_parse_option(buf, &gn_atem_op_csns, data.csns);
}
- /* 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+COPS is PLMN selection */
if (strncasecmp(*buf, "OPS", 3) == 0) {
buf[0] += 3;
@@ -1380,10 +1673,19 @@
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);
@@ -1392,6 +1694,83 @@
return (true);
}
+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);
+ }
+}
+
+/* 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);
+}
+
/* AT+G commands. Some of these responses are a bit tongue in cheek... */
bool gn_atem_command_plusg(char **buf)
{
@@ -1427,11 +1806,21 @@
if (strncasecmp(*buf, "SN", 3) == 0) {
buf[0] += 2;
- gsprintf(buffer, MAX_LINE_LENGTH, _("none built in, choose your own\r\n"));
+ 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);
}
@@ -1473,36 +1862,47 @@
{
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;
+ gn_atem_string_out("OK\r\n");
+ break;
case MR_ERROR:
- gn_atem_string_out("ERROR\r\n");
- break;
+ gn_atem_string_out("ERROR\r\n");
+ break;
case MR_CARRIER:
- gn_atem_string_out("CARRIER\r\n");
- break;
+ gn_atem_string_out("CARRIER\r\n");
+ break;
case MR_CONNECT:
- gn_atem_string_out("CONNECT\r\n");
- break;
+ gn_atem_string_out("CONNECT\r\n");
+ break;
case MR_NOCARRIER:
- gn_atem_string_out("NO CARRIER\r\n");
- break;
+ gn_atem_string_out("NO CARRIER\r\n");
+ break;
+
case MR_RING:
+ if (!strcmp(data.crc, "1"))
+ gn_atem_string_out(
+ "+CRING: VOICE\r\n");
+ else
gn_atem_string_out("RING\r\n");
- break;
+ if (!strcmp(data.clip, "1,1"))
+ gn_atem_string_out("+CLIP: "
+ "\"313373\",0\r\n");
+ break;
+
default:
- gn_atem_string_out(_("\r\nUnknown Result Code!\r\n"));
- break;
+ gn_atem_string_out(_("\r\nUnknown Result Code!\r\n"));
+ break;
}
}
@@ -1530,21 +1930,20 @@
char out_char;
while (*buffer) {
-
/* Translate CR/LF/BS as appropriate */
switch (*buffer) {
- case '\r':
- out_char = ModemRegisters[REG_CR];
- break;
- case '\n':
- out_char = ModemRegisters[REG_LF];
- break;
- case '\b':
- out_char = ModemRegisters[REG_BS];
- break;
- default:
- out_char = *buffer;
- break;
+ case '\r':
+ out_char = ModemRegisters[REG_CR];
+ break;
+ case '\n':
+ out_char = ModemRegisters[REG_LF];
+ break;
+ case '\b':
+ out_char = ModemRegisters[REG_BS];
+ break;
+ default:
+ out_char = *buffer;
+ break;
}
sm->info->write(sm->info->opaque, "%c", out_char);
Modified: trunk/src/host/qemu-neo1973/gnokiigsm/at-emulator.h
===================================================================
--- trunk/src/host/qemu-neo1973/gnokiigsm/at-emulator.h 2007-06-23 00:58:06 UTC (rev 2335)
+++ trunk/src/host/qemu-neo1973/gnokiigsm/at-emulator.h 2007-06-23 01:01:43 UTC (rev 2336)
@@ -44,6 +44,7 @@
void gn_atem_dir_parse(char *cmd_buffer);
bool gn_atem_command_plusc(char **buf);
bool gn_atem_command_plusg(char **buf);
+bool gn_atem_command_percent(char **buf);
int gn_atem_num_get(char **p);
void gn_atem_modem_result(int code);
void gn_atem_call_passup(gn_call_status call_status, gn_call_info *call_info, struct gn_statemachine *state);
Modified: trunk/src/host/qemu-neo1973/gnokiigsm/data.h
===================================================================
--- trunk/src/host/qemu-neo1973/gnokiigsm/data.h 2007-06-23 00:58:06 UTC (rev 2335)
+++ trunk/src/host/qemu-neo1973/gnokiigsm/data.h 2007-06-23 01:01:43 UTC (rev 2336)
@@ -108,23 +108,33 @@
gn_file_list *file_list;
gn_file *file;
+ void (*network_change_notification)(int connected,
+ struct gn_statemachine *state);
+ int connected;
+ void (*signal_quality_notification)(struct gn_statemachine *state);
gn_choice cscs;
gn_choice cmux;
+ gn_choice cmee;
gn_choice ws46;
gn_choice csta;
gn_choice cbst;
gn_choice crlp;
gn_choice cr;
gn_choice crc;
+ gn_choice clip;
gn_choice cmod;
gn_choice csns;
gn_choice creg;
gn_choice cpas;
gn_choice cops;
+ gn_choice colp;
gn_choice cfun;
gn_choice cbc;
+ gn_choice cpi;
gn_choice band;
gn_choice cssn;
+ gn_choice ctzr;
+ int csq;
} gn_data;
/*
@@ -271,6 +281,8 @@
GN_OP_GetFileDetailsById,
GN_OP_GetFileById,
GN_OP_DeleteFileById,
+ GN_OP_NetworkRegister,
+ GN_OP_NetworkUnregister,
GN_OP_Max, /* don't append anything after this entry */
} gn_operation;
Modified: trunk/src/host/qemu-neo1973/gnokiigsm/statemachine.h
===================================================================
--- trunk/src/host/qemu-neo1973/gnokiigsm/statemachine.h 2007-06-23 00:58:06 UTC (rev 2335)
+++ trunk/src/host/qemu-neo1973/gnokiigsm/statemachine.h 2007-06-23 01:01:43 UTC (rev 2336)
@@ -103,6 +103,7 @@
struct gn_statemachine *sm);
void (*write)(void *opaque, const char *fmt, ...);
void *opaque;
+ int non_at_ok;
};
GNOKII_API gn_state gn_sm_loop(int timeout, struct gn_statemachine *state);
Modified: trunk/src/host/qemu-neo1973/hw/modem.c
===================================================================
--- trunk/src/host/qemu-neo1973/hw/modem.c 2007-06-23 00:58:06 UTC (rev 2335)
+++ trunk/src/host/qemu-neo1973/hw/modem.c 2007-06-23 01:01:43 UTC (rev 2336)
@@ -26,6 +26,9 @@
struct gn_statemachine state;
struct gsmmodem_info_s info;
+ QEMUTimer *csq_tm;
+ QEMUTimer *reg_tm;
+ gn_data *reg_data;
};
#define TICALYPSOv3_MANF "<manufacturer>"
@@ -41,10 +44,17 @@
static gn_error modem_ops(gn_operation op, gn_data *data,
struct gn_statemachine *sm)
{
+ struct modem_s *s = (struct modem_s *) sm->info->opaque;
+ int len;
+
switch (op) {
case GN_OP_MakeCall:
- fprintf(stderr, "%s: calling %s: busy.\n", __FUNCTION__,
- data->call_info->number);
+ len = strlen(data->call_info->number);
+ if (data->call_info->number[len - 1] == ';')
+ len --;
+ fprintf(stderr, "%s: calling %.*s: busy.\n", __FUNCTION__,
+ len, data->call_info->number);
+ /* FIXME: this is not the right way to signal busy line. */
return GN_ERR_LINEBUSY;
case GN_OP_CancelCall:
@@ -67,7 +77,7 @@
return GN_ERR_NOTSUPPORTED;
case GN_OP_GetRFLevel:
- *data->rf_level = 32.0f; /* Some -50 dBm */
+ *data->rf_level = 30.0f; /* Some -50 dBm */
break;
case GN_OP_GetImei:
@@ -89,6 +99,18 @@
strcpy(data->manufacturer, TICALYPSOv4_MANF);
break;
+ case GN_OP_NetworkRegister:
+ s->reg_data = data;
+ qemu_mod_timer(s->reg_tm, qemu_get_clock(vm_clock) + ticks_per_sec);
+ break;
+
+ case GN_OP_NetworkUnregister:
+ qemu_del_timer(s->reg_tm);
+ qemu_del_timer(s->csq_tm);
+ if (data->network_change_notification)
+ data->network_change_notification(0, &s->state);
+ break;
+
default:
return GN_ERR_NOTSUPPORTED;
}
@@ -99,8 +121,26 @@
{
s->out_len = 0;
s->baud_delay = ticks_per_sec;
+ qemu_del_timer(s->reg_tm);
+ qemu_del_timer(s->csq_tm);
}
+static void modem_csq_report(void *opaque)
+{
+ struct modem_s *s = (struct modem_s *) opaque;
+ if (s->reg_data && s->reg_data->signal_quality_notification)
+ s->reg_data->signal_quality_notification(&s->state);
+ qemu_mod_timer(s->csq_tm, qemu_get_clock(vm_clock) + ticks_per_sec * 30);
+}
+
+static void modem_network_register(void *opaque)
+{
+ struct modem_s *s = (struct modem_s *) opaque;
+ if (s->reg_data && s->reg_data->network_change_notification)
+ s->reg_data->network_change_notification(1, &s->state);
+ modem_csq_report(opaque);
+}
+
static inline void modem_fifo_wake(struct modem_s *s)
{
if (!s->enable || !s->out_len)
@@ -205,6 +245,9 @@
s->info.write = modem_resp;
s->info.gn_sm_functions = modem_ops;
s->info.opaque = s;
+ s->info.non_at_ok = 1; /* Return OK on non-AT commands. */
+ s->reg_tm = qemu_new_timer(vm_clock, modem_network_register, s);
+ s->csq_tm = qemu_new_timer(vm_clock, modem_csq_report, s);
if (!gn_atem_initialise(&s->state))
goto fail;
More information about the commitlog
mailing list