From cbbf4601211669ededac8d41d6adaec277af3e3b Mon Sep 17 00:00:00 2001 From: erin_yueh Date: Mon, 17 Dec 2007 09:31:02 +0000 Subject: gsmd: conference call support (Sean Chiang) git-svn-id: http://svn.openmoko.org/trunk/src/target/gsm@3657 99fdad57-331a-0410-800a-d7fa5415bdb3 --- include/gsmd/usock.h | 30 ++++++++++++++++++ include/libgsmd/phonebook.h | 6 ---- include/libgsmd/voicecall.h | 27 ++++++++++++++++ src/gsmd/ext_response.c | 18 +++++++---- src/gsmd/gsmd.c | 2 ++ src/gsmd/unsolicited.c | 64 ++++++++++++++++++++++++++------------ src/gsmd/usock.c | 56 +++++++++++++++++++++++++++++++++ src/libgsmd/libgsmd_voicecall.c | 25 +++++++++++++++ src/util/event.c | 7 +++++ src/util/shell.c | 69 ++++++++++++++++++++++++++++++++++++++--- 10 files changed, 267 insertions(+), 37 deletions(-) diff --git a/include/gsmd/usock.h b/include/gsmd/usock.h index 5e07ced..d05f2bc 100644 --- a/include/gsmd/usock.h +++ b/include/gsmd/usock.h @@ -42,6 +42,7 @@ enum gsmd_msg_voicecall_type { GSMD_VOICECALL_VOL_SET = 5, GSMD_VOICECALL_VOL_GET = 6, GSMD_VOICECALL_GET_STAT = 7, + GSMD_VOICECALL_CTRL = 8, }; @@ -81,6 +82,23 @@ enum gsmd_call_mpty { GSMD_CALL_MPTY_YES = 1, }; +/* + * call related supplementary services from 3GPP TS 02.30 4.5.5.1 + * R - Release + * A - Accept + * H - Hold + * M - Multiparty + */ +enum gsmd_call_ctrl_proc { + GSMD_CALL_CTRL_R_HLDS = 0, // 0 + GSMD_CALL_CTRL_UDUB = 1, // 0 + GSMD_CALL_CTRL_R_ACTS_A_HLD_WAIT = 2, // 1 + GSMD_CALL_CTRL_R_ACT_X = 3, // 1x + GSMD_CALL_CTRL_H_ACTS_A_HLD_WAIT = 4, // 2 + GSMD_CALL_CTRL_H_ACTS_EXCEPT_X = 5, // 2x + GSMD_CALL_CTRL_M_HELD = 6, // 3 +}; + /* Handset / MT related commands */ enum gsmd_msg_phone_type { GSMD_PHONE_VOLUME = 1, @@ -360,6 +378,12 @@ struct gsmd_call_status { int is_last; } __attribute__ ((packed)); +/* call status from 3GPP TS 07.07 clause 7.12 */ +struct gsmd_call_ctrl { + enum gsmd_call_ctrl_proc proc; + u_int8_t idx; +} __attribute__ ((packed)); + #define GSMD_PIN_MAXLEN 8 struct gsmd_pin { enum gsmd_pin_type type; @@ -425,6 +449,12 @@ struct gsmd_evt_auxdata { struct { enum gsm0705_cms_error number; } cms_err; + struct { + struct gsmd_addr addr; + u_int8_t classx; + char alpha[16]; + u_int8_t cli; + } ccwa; } u; u_int8_t data[0]; } __attribute__ ((packed)); diff --git a/include/libgsmd/phonebook.h b/include/libgsmd/phonebook.h index d5f8c14..fbc651e 100644 --- a/include/libgsmd/phonebook.h +++ b/include/libgsmd/phonebook.h @@ -84,10 +84,4 @@ extern int lgsm_pb_write_entry(struct lgsm_handle *lh, /* Get the location range/nlength/tlength supported */ extern int lgsm_pb_get_support(struct lgsm_handle *lh); -/* Retrieve the records of READRG request */ -extern int lgsm_pb_retrieve_readrg(struct lgsm_handle *lh, int num); - -/* Retrieve the records of FIND request */ -extern int lgsm_pb_retrieve_find(struct lgsm_handle *lh, int num); - #endif diff --git a/include/libgsmd/voicecall.h b/include/libgsmd/voicecall.h index 2d9609c..ac7b99c 100644 --- a/include/libgsmd/voicecall.h +++ b/include/libgsmd/voicecall.h @@ -5,6 +5,29 @@ /* Voice Calls */ +/* + * call related supplementary services from 3GPP TS 02.30 4.5.5.1 + * R - Release + * A - Accept + * H - Hold + * M - Multiparty + */ +enum lgsm_voicecall_ctrl_proc { + LGSM_VOICECALL_CTRL_R_HLDS = 0, // 0 + LGSM_VOICECALL_CTRL_UDUB = 1, // 0 + LGSM_VOICECALL_CTRL_R_ACTS_A_HLD_WAIT = 2, // 1 + LGSM_VOICECALL_CTRL_R_ACT_X = 3, // 1x + LGSM_VOICECALL_CTRL_H_ACTS_A_HLD_WAIT = 4, // 2 + LGSM_VOICECALL_CTRL_H_ACTS_EXCEPT_X = 5, // 2x + LGSM_VOICECALL_CTRL_M_HELD = 6, // 3 +}; + +/* Refer to GSM 07.07 subclause 7.12 and 02.30 subclause 4.5.5.1 */ +struct lgsm_voicecall_ctrl { + enum lgsm_voicecall_ctrl_proc proc; + int idx; +}; + /* Initiate an outgoing voice call */ extern int lgsm_voice_out_init(struct lgsm_handle *lh, const struct lgsm_addr *number); @@ -21,4 +44,8 @@ extern int lgsm_voice_dtmf(struct lgsm_handle *lh, char dtmf_char); /* Get call status */ extern int lgsm_voice_get_status(struct lgsm_handle *lh); +/* Call control */ +extern int lgsm_voice_ctrl(struct lgsm_handle *lh, + const struct lgsm_voicecall_ctrl *ctrl); + #endif diff --git a/src/gsmd/ext_response.c b/src/gsmd/ext_response.c index 37edf59..f2a94a7 100644 --- a/src/gsmd/ext_response.c +++ b/src/gsmd/ext_response.c @@ -109,16 +109,16 @@ struct gsm_extrsp *extrsp_parse(const void *ctx, const char *input) } break; case TOKEN_NUMERIC: - if (isdigit(*cur)) { - *cur_buf = *cur; - cur_buf++; - } - - if (*cur == ',' || *(cur+1) == '\0') { + if (*cur == ',') { /* end of number */ cur_token->u.numeric = atoi(buf); er->num_tokens++; state = IDLE; + } else if (isdigit(*cur)) { + *cur_buf = *cur; + cur_buf++; + } else { + /* ERORR */ } break; case TOKEN_STRING: @@ -170,6 +170,12 @@ struct gsm_extrsp *extrsp_parse(const void *ctx, const char *input) cur++; } + if (state == TOKEN_NUMERIC) { + /* end of number */ + cur_token->u.numeric = atoi(buf); + er->num_tokens++; + } + //extrsp_dump(er); return er; } diff --git a/src/gsmd/gsmd.c b/src/gsmd/gsmd.c index ce8ec7e..53b43a8 100644 --- a/src/gsmd/gsmd.c +++ b/src/gsmd/gsmd.c @@ -171,6 +171,8 @@ static int gsmd_initsettings2(struct gsmd *gsmd) rc |= gsmd_simplecmd(gsmd, "AT+CLIP=1"); /* use +COLP: to indicate COLP */ rc |= gsmd_simplecmd(gsmd, "AT+COLP=1"); + /* use +CCWA: to indicate waiting call */ + rc |= gsmd_simplecmd(gsmd, "AT+CCWA=1,1"); /* configure message format as PDU mode*/ /* FIXME: TEXT mode support!! */ rc |= gsmd_simplecmd(gsmd, "AT+CMGF=0"); diff --git a/src/gsmd/unsolicited.c b/src/gsmd/unsolicited.c index 6e96b35..8c99183 100644 --- a/src/gsmd/unsolicited.c +++ b/src/gsmd/unsolicited.c @@ -253,33 +253,57 @@ static int creg_parse(char *buf, int len, const char *param, static int ccwa_parse(char *buf, int len, const char *param, struct gsmd *gsmd) { - const char *token; - unsigned int type; + struct gsmd_evt_auxdata *aux; + struct gsm_extrsp *er; struct gsmd_ucmd *ucmd = usock_build_event(GSMD_MSG_EVENT, GSMD_EVT_CALL_WAIT, - sizeof(struct gsmd_addr)); - struct gsmd_addr *gaddr; - + sizeof(struct gsmd_evt_auxdata)); + if (!ucmd) return -ENOMEM; - gaddr = (struct gsmd_addr *) ucmd->buf; - memset(gaddr, 0, sizeof(*gaddr)); + aux = (struct gsmd_evt_auxdata *) ucmd->buf; + + er = extrsp_parse(gsmd_tallocs, param); - /* parse address (phone number) */ - token = strtok(buf, ","); - if (!token) - return -EINVAL; - strncpy(gaddr->number, token, GSMD_ADDR_MAXLEN); + if ( !er ) + return -ENOMEM; - /* parse type */ - token = strtok(NULL, ","); - if (!token) + if ( er->num_tokens == 5 && + er->tokens[0].type == GSMD_ECMD_RTT_STRING && + er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[2].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[3].type == GSMD_ECMD_RTT_EMPTY && + er->tokens[4].type == GSMD_ECMD_RTT_NUMERIC ) { + /* + * ,,,[][,] + */ + + strcpy(aux->u.ccwa.addr.number, er->tokens[0].u.string); + aux->u.ccwa.addr.type = er->tokens[1].u.numeric; + aux->u.ccwa.classx = er->tokens[2].u.numeric; + aux->u.ccwa.alpha[0] = '\0'; + aux->u.ccwa.cli = er->tokens[4].u.numeric; + } + else if ( er->num_tokens == 5 && + er->tokens[0].type == GSMD_ECMD_RTT_STRING && + er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[2].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[3].type == GSMD_ECMD_RTT_STRING && + er->tokens[4].type == GSMD_ECMD_RTT_NUMERIC ) { + /* + * ,,,[][,] + */ + + strcpy(aux->u.ccwa.addr.number, er->tokens[0].u.string); + aux->u.ccwa.addr.type = er->tokens[1].u.numeric; + aux->u.ccwa.classx = er->tokens[2].u.numeric; + strcpy(aux->u.ccwa.alpha, er->tokens[3].u.string); + aux->u.ccwa.cli = er->tokens[4].u.numeric; + } + else { + DEBUGP("Invalid Input : Parse error\n"); return -EINVAL; - type = atoi(token) & 0xff; - gaddr->type = type; - - /* FIXME: parse class */ - token = strtok(NULL, ","); + } return usock_evt_send(gsmd, ucmd, GSMD_EVT_CALL_WAIT); } diff --git a/src/gsmd/usock.c b/src/gsmd/usock.c index 0a4acd7..80f2e01 100644 --- a/src/gsmd/usock.c +++ b/src/gsmd/usock.c @@ -200,6 +200,23 @@ static int voicecall_get_stat_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) cmd->id, sizeof(gcs), &gcs); } +static int voicecall_ctrl_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) +{ + struct gsmd_user *gu = ctx; + int ret = 0; + + DEBUGP("resp: %s\n", resp); + + if ( !strncmp(resp, "+CME", 4) ) { + /* +CME ERROR: */ + DEBUGP("+CME error\n"); + ret = atoi(strpbrk(resp, "0123456789")); + } + + return gsmd_ucmd_submit(gu, GSMD_MSG_VOICECALL, GSMD_VOICECALL_CTRL, + cmd->id, sizeof(ret), &ret); +} + static int usock_ringing_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; @@ -217,6 +234,7 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, struct gsmd_atcmd *cmd = NULL; struct gsmd_addr *ga; struct gsmd_dtmf *gd; + struct gsmd_call_ctrl *gcc; int atcmd_len; switch (gph->msg_subtype) { @@ -274,6 +292,44 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, if (!cmd) return -ENOMEM; break; + case GSMD_VOICECALL_CTRL: + if (len < sizeof(*gph) + sizeof(*gcc)) + return -EINVAL; + + gcc = (struct gsmd_call_ctrl *) ((void *)gph + sizeof(*gph)); + + atcmd_len = 1 + strlen("AT+CHLD=") + 2; + cmd = atcmd_fill("AT+CHLD=", atcmd_len, &voicecall_ctrl_cb, + gu, gph->id, NULL); + if (!cmd) + return -ENOMEM; + + switch (gcc->proc) { + case GSMD_CALL_CTRL_R_HLDS: + case GSMD_CALL_CTRL_UDUB: + sprintf(cmd->buf, "AT+CHLD=%d", 0); + break; + case GSMD_CALL_CTRL_R_ACTS_A_HLD_WAIT: + sprintf(cmd->buf, "AT+CHLD=%d", 1); + break; + case GSMD_CALL_CTRL_R_ACT_X: + sprintf(cmd->buf, "AT+CHLD=%d%d", 1, gcc->idx); + break; + case GSMD_CALL_CTRL_H_ACTS_A_HLD_WAIT: + sprintf(cmd->buf, "AT+CHLD=%d", 2); + break; + case GSMD_CALL_CTRL_H_ACTS_EXCEPT_X: + sprintf(cmd->buf, "AT+CHLD=%d%d", 2, gcc->idx); + break; + case GSMD_CALL_CTRL_M_HELD: + sprintf(cmd->buf, "AT+CHLD=%d", 3); + break; + default: + return -EINVAL; + } + + break; + default: return -EINVAL; } diff --git a/src/libgsmd/libgsmd_voicecall.c b/src/libgsmd/libgsmd_voicecall.c index 214ae0c..8804cce 100644 --- a/src/libgsmd/libgsmd_voicecall.c +++ b/src/libgsmd/libgsmd_voicecall.c @@ -100,3 +100,28 @@ int lgsm_voice_get_status(struct lgsm_handle *lh) { return lgsm_send_simple(lh, GSMD_MSG_VOICECALL, GSMD_VOICECALL_GET_STAT); } + +int lgsm_voice_ctrl(struct lgsm_handle *lh, const struct lgsm_voicecall_ctrl *ctrl) +{ + struct gsmd_msg_hdr *gmh; + struct gsmd_call_ctrl *gcc; + int rc; + + gmh = lgsm_gmh_fill(GSMD_MSG_VOICECALL, + GSMD_VOICECALL_CTRL, sizeof(*gcc)); + if (!gmh) + return -ENOMEM; + gcc = (struct gsmd_call_ctrl *) gmh->data; + gcc->proc = ctrl->proc; + gcc->idx = ctrl->idx; + + rc = lgsm_send(lh, gmh); + if (rc < gmh->len + sizeof(*gmh)) { + lgsm_gmh_free(gmh);; + return -EIO; + } + + lgsm_gmh_free(gmh); + + return 0; +} diff --git a/src/util/event.c b/src/util/event.c index 24a8924..d063642 100644 --- a/src/util/event.c +++ b/src/util/event.c @@ -194,6 +194,12 @@ static int sigq_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata return 0; } +static int ccwa_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata *aux) +{ + printf("EVENT: Call Waiting: %s,%d\n", aux->u.ccwa.addr.number, aux->u.ccwa.addr.type); + return 0; +} + static const char *cprog_names[] = { [GSMD_CALLPROG_SETUP] = "SETUP", [GSMD_CALLPROG_DISCONNECT] = "DISCONNECT", @@ -257,6 +263,7 @@ int event_init(struct lgsm_handle *lh) rc |= lgsm_evt_handler_register(lh, GSMD_EVT_SIGNAL, &sigq_handler); rc |= lgsm_evt_handler_register(lh, GSMD_EVT_OUT_STATUS, &cprog_handler); rc |= lgsm_evt_handler_register(lh, GSMD_EVT_IN_ERROR, &error_handler); + rc |= lgsm_evt_handler_register(lh, GSMD_EVT_CALL_WAIT, &ccwa_handler); return rc; } diff --git a/src/util/shell.c b/src/util/shell.c index fec445e..e621e33 100644 --- a/src/util/shell.c +++ b/src/util/shell.c @@ -398,6 +398,7 @@ static int pin_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh) static int call_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh) { struct gsmd_call_status *gcs; + int *ret; switch (gmh->msg_subtype) { case GSMD_VOICECALL_GET_STAT: @@ -419,10 +420,14 @@ static int call_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh) if (gcs->is_last) pending_responses --; break; + case GSMD_VOICECALL_CTRL: + ret = (int*)((char *)gmh + sizeof(*gmh)); + (*ret)? printf("+CME ERROR %d\n", *ret) : printf("OK\n"); + pending_responses --; + break; default: return -EINVAL; } - pending_responses --; return 0; } @@ -479,6 +484,13 @@ static int shell_help(void) "\tim\tGet imsi\n" "\tcs\tGet Call status\n" "\tgp\tGet PIN status\n" + "\tRh\tRelease all held calls (+CHLD=0)\n" + "\tUDUB\tUser Determined User Busy (+CHLD=0)\n" + "\tRa\tRelease all active calls (+CHLD=1)\n" + "\tRx\tRelease specific active call x (Rx=x)(+CHLD=1x)\n" + "\tHa\tHold all active calls and accept held or waiting call (+CHLD=2)\n" + "\tHx\tHold all active calls except call x (Hx=x)(+CHLD=2x)\n" + "\tMP\tAdd a held call to the conversation (+CHLD=3)\n" "\tq\tQuit\n" ); } @@ -562,7 +574,7 @@ int shell_main(struct lgsm_handle *lgsmh, int sync) } else if (!strcmp(buf, "r")) { printf("Register\n"); lgsm_netreg_register(lgsmh, "\0 "); - } else if (buf[0] == 'R') { + } else if (!strcmp(buf,"R")) { printf("Register to operator\n"); ptr = strchr(buf, '='); if (!ptr || strlen(ptr) < 6) @@ -600,7 +612,7 @@ int shell_main(struct lgsm_handle *lgsmh, int sync) printf("DTMF: %c\n", buf[1]); lgsm_voice_dtmf(lgsmh, buf[1]); } else if ( !strncmp(buf, "pd", 2)) { - printf("Delete Phonebook Entry\n"); + printf("Delete Phonebook Entry\n"); ptr = strchr(buf, '='); lgsm_pb_del_entry(lgsmh, atoi(ptr+1)); } else if ( !strncmp(buf, "prr", 3)) { @@ -642,7 +654,7 @@ int shell_main(struct lgsm_handle *lgsmh, int sync) if ('+' == pb.numb[0]) pb.type = LGSM_PB_ATYPE_INTL; else - pb.type = LGSM_PB_ATYPE_OTHE; + pb.type = LGSM_PB_ATYPE_OTHE; strncpy(pb.text, lcomma + 1, strlen(lcomma + 1)); pb.text[strlen(lcomma + 1)] = '\0'; @@ -758,7 +770,7 @@ int shell_main(struct lgsm_handle *lgsmh, int sync) printf("Get imsi\n"); lgsm_get_imsi(lgsmh); pending_responses ++; - } else if (!strncmp(buf, "M", 1)) { + } else if ( strlen(buf)==1 && !strncmp(buf, "M", 1)) { printf("Modem Power On\n"); lgsm_modem_power(lgsmh, 1); pending_responses ++; @@ -774,6 +786,53 @@ int shell_main(struct lgsm_handle *lgsmh, int sync) printf("Get PIN status\n"); lgsm_pin_status(lgsmh); pending_responses ++; + } else if ( !strncmp(buf, "Rh", 2)) { + struct lgsm_voicecall_ctrl ctrl; + ctrl.proc = LGSM_VOICECALL_CTRL_R_HLDS; + printf("Release all held calls\n"); + lgsm_voice_ctrl(lgsmh, &ctrl); + pending_responses ++; + } else if ( !strncmp(buf, "UDUB", 4)) { + struct lgsm_voicecall_ctrl ctrl; + ctrl.proc = LGSM_VOICECALL_CTRL_UDUB; + printf("User Determined User Busy\n"); + lgsm_voice_ctrl(lgsmh, &ctrl); + pending_responses ++; + } else if ( !strncmp(buf, "Ra", 2)) { + struct lgsm_voicecall_ctrl ctrl; + ctrl.proc = LGSM_VOICECALL_CTRL_R_ACTS_A_HLD_WAIT; + printf("Release all active calls\n"); + lgsm_voice_ctrl(lgsmh, &ctrl); + pending_responses ++; + } else if ( !strncmp(buf, "Rx", 2)) { + struct lgsm_voicecall_ctrl ctrl; + ctrl.proc = LGSM_VOICECALL_CTRL_R_ACT_X; + printf("Release specific active call x\n"); + ptr = strchr(buf, '='); + ctrl.idx = atoi(ptr+1); + lgsm_voice_ctrl(lgsmh, &ctrl); + pending_responses ++; + } else if ( !strncmp(buf, "Ha", 2)) { + struct lgsm_voicecall_ctrl ctrl; + ctrl.proc = LGSM_VOICECALL_CTRL_H_ACTS_A_HLD_WAIT; + printf("Hold all active calls and accept held or waiting" + " call\n"); + lgsm_voice_ctrl(lgsmh, &ctrl); + pending_responses ++; + } else if ( !strncmp(buf, "Hx", 2)) { + struct lgsm_voicecall_ctrl ctrl; + ctrl.proc = LGSM_VOICECALL_CTRL_H_ACTS_EXCEPT_X; + printf("Hold all active calls except call x\n"); + ptr = strchr(buf, '='); + ctrl.idx = atoi(ptr+1); + lgsm_voice_ctrl(lgsmh, &ctrl); + pending_responses ++; + } else if ( !strncmp(buf, "MP", 2)) { + struct lgsm_voicecall_ctrl ctrl; + ctrl.proc = LGSM_VOICECALL_CTRL_M_HELD; + printf("Add a held call to the conversation\n"); + lgsm_voice_ctrl(lgsmh, &ctrl); + pending_responses ++; } else { printf("Unknown command `%s'\n", buf); } -- cgit v1.2.3