Index: common/gsm-call.c =================================================================== RCS file: /sources/gnokii/gnokii/common/gsm-call.c,v retrieving revision 1.15 diff -u -p -r1.15 gsm-call.c --- common/gsm-call.c 5 Jul 2006 21:51:46 -0000 1.15 +++ common/gsm-call.c 6 Jul 2007 12:45:10 -0000 @@ -181,7 +181,7 @@ GNOKII_API gn_call *gn_call_get_active(i GNOKII_API gn_error gn_call_check_active(struct gn_statemachine *state) { gn_data data; - gn_call_active active[2]; + gn_call_active active[GN_CALL_MAX_PARALLEL]; gn_call *call; gn_error err; int i, j, got; @@ -197,7 +197,7 @@ GNOKII_API gn_error gn_call_check_active for (j = 0; j < GN_CALL_MAX_PARALLEL; j++) { if (calltable[j].state != state) continue; got = 0; - for ( i = 0; i < 2; i++) { + for ( i = 0; i < GN_CALL_MAX_PARALLEL; i++) { if (calltable[j].call_id == active[i].call_id) { got = 1; break; @@ -209,7 +209,7 @@ GNOKII_API gn_error gn_call_check_active } } - for (i = 0; i < 2; i++) { + for (i = 0; i < GN_CALL_MAX_PARALLEL; i++) { if (active[i].state == GN_CALL_Idle) continue; if (!(call = search_call(active[i].call_id, state))) { Index: common/gsm-sms.c =================================================================== RCS file: /sources/gnokii/gnokii/common/gsm-sms.c,v retrieving revision 1.159 diff -u -p -r1.159 gsm-sms.c --- common/gsm-sms.c 6 Jan 2007 22:53:21 -0000 1.159 +++ common/gsm-sms.c 6 Jul 2007 12:45:10 -0000 @@ -747,6 +747,8 @@ GNOKII_API gn_error gn_sms_get(gn_data * gn_sms_raw rawsms; if (!data->sms) return GN_ERR_INTERNALERROR; + if (data->sms->number < 1) return GN_ERR_EMPTYLOCATION; + if (data->sms->memory_type > GN_MT_LAST) return GN_ERR_INVALIDMEMORYTYPE; memset(&rawsms, 0, sizeof(gn_sms_raw)); rawsms.number = data->sms->number; rawsms.memory_type = data->sms->memory_type; Index: common/links/atbus.c =================================================================== RCS file: /sources/gnokii/gnokii/common/links/atbus.c,v retrieving revision 1.49 diff -u -p -r1.49 atbus.c --- common/links/atbus.c 26 Apr 2006 20:07:30 -0000 1.49 +++ common/links/atbus.c 6 Jul 2007 12:45:10 -0000 @@ -177,6 +177,7 @@ static void atbus_rx_statemachine(unsign /* if not found, start at buffer beginning */ if (!start) start = bi->rbuf+1; + /* there are certainly more that 2 chars in buffer */ if (!strncmp(start, "OK", 2)) bi->rbuf[0] = GN_AT_OK; @@ -219,6 +220,12 @@ static void atbus_rx_statemachine(unsign else if (!strncmp(start + 1, "CRING:", 6)) { sm_incoming_function(GN_OP_AT_Ring, start, bi->rbuf_pos - 1 - (start - bi->rbuf), sm); unsolicited = 1; + } else if (!strncmp(start + 1, "CLIP:", 5)) { + sm_incoming_function(GN_OP_AT_Ring, start, bi->rbuf_pos - 1 - (start - bi->rbuf), sm); + unsolicited = 1; + } else if (!strncmp(start + 1, "CLCC:", 5)) { + sm_incoming_function(GN_OP_AT_Ring, start, bi->rbuf_pos - 1 - (start - bi->rbuf), sm); + unsolicited = 1; } } if (unsolicited) { Index: common/phones/atgen.c =================================================================== RCS file: /sources/gnokii/gnokii/common/phones/atgen.c,v retrieving revision 1.145 diff -u -p -r1.145 atgen.c --- common/phones/atgen.c 5 Jul 2007 21:28:40 -0000 1.145 +++ common/phones/atgen.c 6 Jul 2007 12:45:11 -0000 @@ -76,6 +76,7 @@ static gn_error ReplyGetSecurityCodeStat static gn_error ReplyGetNetworkInfo(int messagetype, unsigned char *buffer, int length, gn_data *data, struct gn_statemachine *state); static gn_error ReplyRing(int messagetype, unsigned char *buffer, int length, gn_data *data, struct gn_statemachine *state); static gn_error ReplyGetDateTime(int messagetype, unsigned char *buffer, int length, gn_data *data, struct gn_statemachine *state); +static gn_error ReplyGetActiveCalls(int messagetype, unsigned char *buffer, int length, gn_data *data, struct gn_statemachine *state); static gn_error AT_Identify(gn_data *data, struct gn_statemachine *state); static gn_error AT_GetModel(gn_data *data, struct gn_statemachine *state); @@ -112,6 +113,7 @@ static gn_error AT_SetCallNotification(g static gn_error AT_SetDateTime(gn_data *data, struct gn_statemachine *state); static gn_error AT_GetDateTime(gn_data *data, struct gn_statemachine *state); static gn_error AT_SendDTMF(gn_data *data, struct gn_statemachine *state); +static gn_error AT_GetActiveCalls(gn_data *data, struct gn_statemachine *state); typedef struct { int gop; @@ -160,6 +162,7 @@ static at_function_init_type at_function { GN_OP_SetDateTime, AT_SetDateTime, Reply }, { GN_OP_GetDateTime, AT_GetDateTime, ReplyGetDateTime }, { GN_OP_SendDTMF, AT_SendDTMF, Reply }, + { GN_OP_GetActiveCalls, AT_GetActiveCalls, ReplyGetActiveCalls }, }; char *strip_quotes(char *s) @@ -1130,6 +1133,16 @@ static gn_error AT_SetCallNotification(g return GN_ERR_NOTREADY; if ((err = sm_block_no_retry(GN_OP_SetCallNotification, data, state)) != GN_ERR_NONE) return err; + if (sm_message_send(10, GN_OP_SetCallNotification, "AT+CLIP=1\r", state)) + return GN_ERR_NOTREADY; + /* Ignore errors when we can't set Call Line Identity, just set that we + * don't handle it */ + if (sm_block_no_retry(GN_OP_SetCallNotification, data, state) == GN_ERR_NONE) + drvinst->clip_supported = 1; + if (sm_message_send(10, GN_OP_SetCallNotification, "AT+CLCC=1\r", state)) + return GN_ERR_NOTREADY; + /* Ignore errors when we can't set List Current Calls notifications */ + sm_block_no_retry(GN_OP_SetCallNotification, data, state); } drvinst->call_notification = data->call_notification; @@ -1223,6 +1236,13 @@ static gn_error AT_SendDTMF(gn_data *dat return error; } +static gn_error AT_GetActiveCalls(gn_data *data, struct gn_statemachine *state) +{ + if (sm_message_send(8, GN_OP_GetActiveCalls, "AT+CPAS\r", state)) + return GN_ERR_NOTREADY; + return sm_block_no_retry(GN_OP_GetActiveCalls, data, state); +} + static gn_error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, gn_data *data, struct gn_statemachine *state) { at_driver_instance *drvinst = AT_DRVINST(state); @@ -1771,6 +1791,7 @@ static gn_error ReplyRing(int messagetyp at_line_buffer buf; char *pos; gn_call_info cinfo; + gn_call_status status; if (!drvinst->call_notification) return GN_ERR_UNSOLICITED; @@ -1781,35 +1802,159 @@ static gn_error ReplyRing(int messagetyp memset(&cinfo, 0, sizeof(cinfo)); cinfo.call_id = 1; - if (!strncmp(buf.line1, "RING", 4)) + if (!strncmp(buf.line1, "RING", 4)) { return GN_ERR_INTERNALERROR; /* AT+CRC=1 disables RING */ - - else if (!strncmp(buf.line1, "+CRING: ", 8)) { + } else if (!strncmp(buf.line1, "+CRING: ", 8)) { pos = buf.line1 + 8; if (!strncmp(pos, "VOICE", 5)) cinfo.type = GN_CALL_Voice; else return GN_ERR_UNHANDLEDFRAME; - drvinst->call_notification(GN_CALL_Incoming, &cinfo, state, drvinst->call_callback_data); - - } else if (!strncmp(buf.line1, "CONNECT", 7)) - drvinst->call_notification(GN_CALL_Established, &cinfo, state, drvinst->call_callback_data); + status = GN_CALL_Incoming; + } else if (!strncmp(buf.line1, "CONNECT", 7)) { + status = GN_CALL_Established; + } else if (!strncmp(buf.line1, "BUSY", 4)) { + status = GN_CALL_RemoteHangup; + } else if (!strncmp(buf.line1, "NO ANSWER", 9)) { + status = GN_CALL_RemoteHangup; + } else if (!strncmp(buf.line1, "NO CARRIER", 10)) { + status = GN_CALL_RemoteHangup; + } else if (!strncmp(buf.line1, "NO DIALTONE", 11)) { + status = GN_CALL_LocalHangup; + } else if (!strncmp(buf.line1, "+CLIP: ", 7)) { + char **items; + int i; + + items = gnokii_strsplit(buf.line1 + 7, ",", 6); + for (i = 0; i < 6; i++) { + if (items[i] == NULL) + break; + switch (i) { + /* Number, if known */ + case 0: + snprintf(cinfo.number, GN_PHONEBOOK_NUMBER_MAX_LENGTH, "%s", strip_quotes (items[i])); + break; + /* 1 == Number "type" + * 2 == String type subaddress + * 3 == Type of subaddress + * All ignored + */ + case 1: + case 2: + case 3: + break; + /* Name, if known */ + case 4: + snprintf(cinfo.name, GN_PHONEBOOK_NAME_MAX_LENGTH, "%s", strip_quotes (items[i])); + break; + /* Validity of the name/number provided */ + case 5: + switch (atoi(items[i])) { + case 1: + snprintf(cinfo.name, GN_PHONEBOOK_NAME_MAX_LENGTH, "Withheld"); + break; + case 2: + snprintf(cinfo.name, GN_PHONEBOOK_NAME_MAX_LENGTH, "Unknown"); + break; + } + } + } - else if (!strncmp(buf.line1, "BUSY", 4)) - drvinst->call_notification(GN_CALL_RemoteHangup, &cinfo, state, drvinst->call_callback_data); + if (cinfo.name == NULL) + snprintf(cinfo.name, GN_PHONEBOOK_NAME_MAX_LENGTH, "Unknown"); + cinfo.type = drvinst->last_call_type; + drvinst->call_notification(drvinst->last_call_status, &cinfo, state, drvinst->call_callback_data); + gnokii_strfreev(items); + return GN_ERR_UNSOLICITED; + } else if (!strncmp(buf.line1, "+CLCC: ", 7)) { + char **items; + int i; - else if (!strncmp(buf.line1, "NO ANSWER", 9)) - drvinst->call_notification(GN_CALL_RemoteHangup, &cinfo, state, drvinst->call_callback_data); + items = gnokii_strsplit(buf.line1 + 7, ",", 8); + status = -1; - else if (!strncmp(buf.line1, "NO CARRIER", 10)) - drvinst->call_notification(GN_CALL_RemoteHangup, &cinfo, state, drvinst->call_callback_data); + for (i = 0; i < 8; i++) { + if (items[i] == NULL) + break; + switch (i) { + /* Call ID */ + case 0: + cinfo.call_id = atoi (items[i]); + break; + /* Incoming, or finishing call */ + case 1: + break; + case 2: + switch (atoi (items[i])) { + case 0: + status = GN_CALL_Established; + break; + case 1: + status = GN_CALL_Held; + break; + case 2: + status = GN_CALL_Dialing; + break; + case 3: + /* Alerting, remote end is ringing */ + status = GN_CALL_Ringing; + break; + case 4: + status = GN_CALL_Incoming; + break; + case 5: + /* FIXME Call is "Waiting" */ + status = GN_CALL_Held; + break; + case 6: + status = GN_CALL_LocalHangup; + break; + } + break; + case 3: + if (atoi(items[i]) == 0) + cinfo.type = GN_CALL_Voice; + else { + /* We don't handle non-voice calls */ + gnokii_strfreev(items); + return GN_ERR_UNHANDLEDFRAME; + } + break; + /* Multiparty status */ + case 4: + break; + case 5: + snprintf(cinfo.number, GN_PHONEBOOK_NUMBER_MAX_LENGTH, "%s", strip_quotes (items[i])); + break; + /* Phone display format */ + case 6: + break; + case 7: + snprintf(cinfo.name, GN_PHONEBOOK_NAME_MAX_LENGTH, "%s", strip_quotes (items[i])); + break; + } + } - else if (!strncmp(buf.line1, "NO DIALTONE", 11)) - drvinst->call_notification(GN_CALL_LocalHangup, &cinfo, state, drvinst->call_callback_data); + /* FIXME we don't handle calls > 1 anywhere else */ + if (status < 0) { + gnokii_strfreev(items); + return GN_ERR_UNHANDLEDFRAME; + } - else + drvinst->call_notification(status, &cinfo, state, drvinst->call_callback_data); + gnokii_strfreev(items); + return GN_ERR_UNSOLICITED; + } else { return GN_ERR_UNHANDLEDFRAME; + } + + if (!drvinst->clip_supported || status != GN_CALL_Incoming) { + drvinst->call_notification(status, &cinfo, state, drvinst->call_callback_data); + } else { + drvinst->last_call_status = status; + drvinst->last_call_type = cinfo.type; + } return GN_ERR_UNSOLICITED; } @@ -1950,6 +2095,73 @@ static gn_error Reply(int messagetype, u return at_error_get(buffer, state); } +static gn_error ReplyGetActiveCalls(int messagetype, unsigned char *buffer, int length, gn_data *data, struct gn_statemachine *state) +{ + at_driver_instance *drvinst; + gn_error error; + at_line_buffer buf; + int status; + + if ((error = at_error_get(buffer, state)) != GN_ERR_NONE) return error; + + status = -1; + + buf.line1 = buffer + 1; + buf.length = length; + splitlines(&buf); + + memset(data->call_active, 0, GN_CALL_MAX_PARALLEL * sizeof(gn_call_active)); + + if (strncmp(buf.line1, "AT+CPAS: ", 6)) { + return GN_ERR_UNKNOWN; + } + + data->call_active->call_id = 1; + + switch (atoi(buf.line2 + strlen ("+CPAS: "))) { + case 0: + status = GN_CALL_Idle; + break; + /* Terminal doesn't know */ + case 1: + case 2: + break; + case 3: + status = GN_CALL_Ringing; + break; + case 4: + status = GN_CALL_Established; + break; + /* Low-power state, terminal can't answer */ + case 5: + break; + } + + if (status < 0) + return GN_ERR_UNKNOWN; + + drvinst = AT_DRVINST(state); + + data->call_active->state = status; + data->call_active->prev_state = drvinst->prev_state; + + /* States go: + * Idle -> Ringing -> (faked Local hangup) -> Idle + * Idle -> Ringing -> Established -> (faked Remote hangup) -> Idle */ + if (drvinst->prev_state == GN_CALL_Ringing && status == GN_CALL_Idle) + data->call_active->state = GN_CALL_LocalHangup; + else if (drvinst->prev_state == GN_CALL_Established && status == GN_CALL_Idle) + data->call_active->state = GN_CALL_RemoteHangup; + else + data->call_active->state = status; + + drvinst->prev_state = data->call_active->state; + snprintf(data->call_active->name, GN_PHONEBOOK_NAME_MAX_LENGTH, "Unknown"); + data->call_active->number[0] = '\0'; + + return GN_ERR_NONE; +} + static gn_error Initialise(gn_data *setupdata, struct gn_statemachine *state) { at_driver_instance *drvinst; @@ -1987,6 +2199,9 @@ static gn_error Initialise(gn_data *setu drvinst->cb_callback_data = NULL; drvinst->on_sms = NULL; drvinst->sms_callback_data = NULL; + drvinst->clip_supported = 0; + drvinst->last_call_type = GN_CALL_Voice; + drvinst->last_call_status = GN_CALL_Idle; drvinst->if_pos = 0; for (i = 0; i < GN_OP_AT_Max; i++) { Index: include/gnokii/common.h =================================================================== RCS file: /sources/gnokii/gnokii/include/gnokii/common.h,v retrieving revision 1.152 diff -u -p -r1.152 common.h --- include/gnokii/common.h 4 May 2007 19:41:34 -0000 1.152 +++ include/gnokii/common.h 6 Jul 2007 12:45:13 -0000 @@ -102,6 +102,7 @@ typedef enum { GN_MT_F18, GN_MT_F19, GN_MT_F20, /* 20th CUSTOM FOLDER */ + GN_MT_LAST = GN_MT_F20, GN_MT_XX = 0xff /* Error code for unknown memory type (returned by fbus-xxxx functions). */ } gn_memory_type; Index: include/phones/atgen.h =================================================================== RCS file: /sources/gnokii/gnokii/include/phones/atgen.h,v retrieving revision 1.24 diff -u -p -r1.24 atgen.h --- include/phones/atgen.h 5 Jul 2007 21:28:39 -0000 1.24 +++ include/phones/atgen.h 6 Jul 2007 12:45:13 -0000 @@ -80,6 +80,14 @@ typedef struct { at_charset defaultcharset; at_charset charset; + /* For call notifications via AT+CLIP */ + int clip_supported; + gn_call_type last_call_type; + gn_call_status last_call_status; + + /* For AT+CPAS */ + gn_call_status prev_state; + /* callbacks */ void (*on_cell_broadcast)(gn_cb_message *msg, void *callback_data); void (*call_notification)(gn_call_status call_status, gn_call_info *call_info, struct gn_statemachine *state, void *callback_data);