diff options
-rw-r--r-- | include/gsmd/usock.h | 38 | ||||
-rw-r--r-- | include/libgsmd/voicecall.h | 35 | ||||
-rw-r--r-- | src/gsmd/usock.c | 148 | ||||
-rw-r--r-- | src/libgsmd/libgsmd_voicecall.c | 123 | ||||
-rw-r--r-- | src/util/shell.c | 57 |
5 files changed, 399 insertions, 2 deletions
diff --git a/include/gsmd/usock.h b/include/gsmd/usock.h index d05f2bc..ddec870 100644 --- a/include/gsmd/usock.h +++ b/include/gsmd/usock.h @@ -43,6 +43,11 @@ enum gsmd_msg_voicecall_type { GSMD_VOICECALL_VOL_GET = 6, GSMD_VOICECALL_GET_STAT = 7, GSMD_VOICECALL_CTRL = 8, + GSMD_VOICECALL_FWD_DIS = 9, + GSMD_VOICECALL_FWD_EN = 10, + GSMD_VOICECALL_FWD_STAT = 11, + GSMD_VOICECALL_FWD_REG = 12, + GSMD_VOICECALL_FWD_ERAS = 13, }; @@ -99,6 +104,22 @@ enum gsmd_call_ctrl_proc { GSMD_CALL_CTRL_M_HELD = 6, // 3 }; +/* call forward reason from 3GPP TS 07.07 subclause 07.10 */ +enum gsmd_call_fwd_reason { + GSMD_CALL_FWD_REASON_UNCOND = 0, + GSMD_CALL_FWD_REASON_BUSY = 1, + GSMD_CALL_FWD_REASON_NO_REPLY = 2, + GSMD_CALL_FWD_REASON_NOT_REACHABLE = 3, + GSMD_CALL_FWD_REASON_ALL_FORWARD = 4, + GSMD_CALL_FWD_REASON_ALL_COND_FORWARD = 5, +}; + +/* call forward status from 3GPP TS 07.07 subclause 07.10 */ +enum gsmd_call_fwd_status { + GSMD_CALL_FWD_STATUS_NOT_ACTIVE = 0, + GSMD_CALL_FWD_STATUS_ACTIVE = 1, +}; + /* Handset / MT related commands */ enum gsmd_msg_phone_type { GSMD_PHONE_VOLUME = 1, @@ -384,6 +405,23 @@ struct gsmd_call_ctrl { u_int8_t idx; } __attribute__ ((packed)); +/* call forwarding register from 3GPP TS 07.07 clause 7.10 */ +struct gsmd_call_fwd_reg { + enum gsmd_call_fwd_reason reason; + struct gsmd_addr addr; +} __attribute__ ((packed)); + +/* status of call forwarding from 3GPP TS 07.07 clause 7.10 */ +struct gsmd_call_fwd_stat { + enum gsmd_call_fwd_status status; + u_int8_t classx; + struct gsmd_addr addr; + char subaddr[16+1]; + u_int8_t satype; + u_int8_t time; + int is_last; +} __attribute__ ((packed)); + #define GSMD_PIN_MAXLEN 8 struct gsmd_pin { enum gsmd_pin_type type; diff --git a/include/libgsmd/voicecall.h b/include/libgsmd/voicecall.h index ac7b99c..87ecfcc 100644 --- a/include/libgsmd/voicecall.h +++ b/include/libgsmd/voicecall.h @@ -22,12 +22,28 @@ enum lgsm_voicecall_ctrl_proc { LGSM_VOICECALL_CTRL_M_HELD = 6, // 3 }; +/* call forward reason from 3GPP TS 07.07 subclause 07.10 */ +enum lgsmd_voicecall_fwd_reason { + GSMD_VOICECALL_FWD_REASON_UNCOND = 0, + GSMD_VOICECALL_FWD_REASON_BUSY = 1, + GSMD_VOICECALL_FWD_REASON_NO_REPLY = 2, + GSMD_VOICECALL_FWD_REASON_NOT_REACHABLE = 3, + GSMD_VOICECALL_FWD_REASON_ALL_FORWARD = 4, + GSMD_VOICECALL_FWD_REASON_ALL_COND_FORWARD = 5, +}; + /* 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; }; +/* Refer to GSM 07.07 subclause 07.10 */ +struct lgsm_voicecall_fwd_reg { + enum lgsmd_voicecall_fwd_reason reason; + struct lgsm_addr number; +}; + /* Initiate an outgoing voice call */ extern int lgsm_voice_out_init(struct lgsm_handle *lh, const struct lgsm_addr *number); @@ -48,4 +64,23 @@ extern int lgsm_voice_get_status(struct lgsm_handle *lh); extern int lgsm_voice_ctrl(struct lgsm_handle *lh, const struct lgsm_voicecall_ctrl *ctrl); +/* disable call forwarding */ +extern int lgsm_voice_fwd_disable(struct lgsm_handle *lh, + enum lgsmd_voicecall_fwd_reason reason); + +/* enable call forwarding */ +extern int lgsm_voice_fwd_enable(struct lgsm_handle *lh, + enum lgsmd_voicecall_fwd_reason reason); + +/* querty current status/setting of call forwarding */ +extern int lgsm_voice_fwd_stat(struct lgsm_handle *lh, + enum lgsmd_voicecall_fwd_reason reason); + +/* register call forwarding */ +extern int lgsm_voice_fwd_reg(struct lgsm_handle *lh, + struct lgsm_voicecall_fwd_reg *fwd_reg); + +/* erase the record of registered call forwarding */ +extern int lgsm_voice_fwd_erase(struct lgsm_handle *lh, + enum lgsmd_voicecall_fwd_reason reason); #endif diff --git a/src/gsmd/usock.c b/src/gsmd/usock.c index c7a0224..5caedac 100644 --- a/src/gsmd/usock.c +++ b/src/gsmd/usock.c @@ -217,6 +217,75 @@ static int voicecall_ctrl_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) cmd->id, sizeof(ret), &ret); } +static int voicecall_fwd_stat_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) +{ + struct gsmd_user *gu = ctx; + struct gsm_extrsp *er; + struct gsmd_call_fwd_stat gcfs; + int ret = 0; + + DEBUGP("resp: %s\n", resp); + + er = extrsp_parse(cmd, resp); + + if ( !er ) + return -ENOMEM; + + gcfs.is_last = (cmd->ret == 0 || cmd->ret == 4)? 1:0; + + if ( er->num_tokens == 2 && + er->tokens[0].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC ) { + + /* + * +CCFC: <status>,<class1>[,<number>,<type> + * [,<subaddr>,<satype>[,<time>]]][ + * <CR><LF>+CCFC: <status>,<class2>[,<number>,<type> + * [,<subaddr>,<satype>[,<time>]]] + * [...]] + */ + + gcfs.status = er->tokens[0].u.numeric; + gcfs.classx = er->tokens[1].u.numeric; + gcfs.addr.number[0] = '\0'; + } + else if ( er->num_tokens == 4 && + er->tokens[0].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[2].type == GSMD_ECMD_RTT_STRING && + er->tokens[3].type == GSMD_ECMD_RTT_NUMERIC ) { + + gcfs.status = er->tokens[0].u.numeric; + gcfs.classx = er->tokens[1].u.numeric; + strcpy(gcfs.addr.number, er->tokens[2].u.string); + gcfs.addr.type = er->tokens[3].u.numeric; + } + else if ( er->num_tokens == 7 && + er->tokens[0].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[2].type == GSMD_ECMD_RTT_STRING && + er->tokens[3].type == GSMD_ECMD_RTT_NUMERIC && + er->tokens[4].type == GSMD_ECMD_RTT_EMPTY && + er->tokens[5].type == GSMD_ECMD_RTT_EMPTY && + er->tokens[6].type == GSMD_ECMD_RTT_NUMERIC ) { + + gcfs.status = er->tokens[0].u.numeric; + gcfs.classx = er->tokens[1].u.numeric; + strcpy(gcfs.addr.number, er->tokens[2].u.string); + gcfs.addr.type = er->tokens[3].u.numeric; + gcfs.time = er->tokens[6].u.numeric; + } + else { + DEBUGP("Invalid Input : Parse error\n"); + return -EINVAL; + } + + talloc_free(er); + + return gsmd_ucmd_submit(gu, GSMD_MSG_VOICECALL, GSMD_VOICECALL_FWD_STAT, + cmd->id, sizeof(gcfs), &gcfs); +} + static int usock_ringing_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; @@ -235,7 +304,10 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, struct gsmd_addr *ga; struct gsmd_dtmf *gd; struct gsmd_call_ctrl *gcc; + struct gsmd_call_fwd_reg *gcfr; + char buf[64]; int atcmd_len; + int *reason; switch (gph->msg_subtype) { case GSMD_VOICECALL_DIAL: @@ -329,7 +401,81 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, } break; - + case GSMD_VOICECALL_FWD_DIS: + if(len < sizeof(*gph) + sizeof(int)) + return -EINVAL; + + reason = (int *) ((void *)gph + sizeof(*gph)); + + sprintf(buf, "%d,0", *reason); + + atcmd_len = 1 + strlen("AT+CCFC=") + strlen(buf); + cmd = atcmd_fill("AT+CCFC=", atcmd_len, + &usock_cmd_cb, gu, gph->id, NULL); + if (!cmd) + return -ENOMEM; + sprintf(cmd->buf, "AT+CCFC=%s", buf); + break; + case GSMD_VOICECALL_FWD_EN: + if(len < sizeof(*gph) + sizeof(int)) + return -EINVAL; + + reason = (int *) ((void *)gph + sizeof(*gph)); + + sprintf(buf, "%d,1", *reason); + + atcmd_len = 1 + strlen("AT+CCFC=") + strlen(buf); + cmd = atcmd_fill("AT+CCFC=", atcmd_len, + &usock_cmd_cb, gu, gph->id, NULL); + if (!cmd) + return -ENOMEM; + sprintf(cmd->buf, "AT+CCFC=%s", buf); + break; + case GSMD_VOICECALL_FWD_STAT: + if(len < sizeof(*gph) + sizeof(int)) + return -EINVAL; + + reason = (int *) ((void *)gph + sizeof(*gph)); + + sprintf(buf, "%d,2", *reason); + + atcmd_len = 1 + strlen("AT+CCFC=") + strlen(buf); + cmd = atcmd_fill("AT+CCFC=", atcmd_len, + &voicecall_fwd_stat_cb, gu, gph->id, NULL); + if (!cmd) + return -ENOMEM; + sprintf(cmd->buf, "AT+CCFC=%s", buf); + break; + case GSMD_VOICECALL_FWD_REG: + if(len < sizeof(*gph) + sizeof(int)) + return -EINVAL; + + gcfr = (struct gsmd_call_fwd_reg *) ((void *)gph + sizeof(*gph)); + + sprintf(buf, "%d,3,\"%s\"", gcfr->reason, gcfr->addr.number); + + atcmd_len = 1 + strlen("AT+CCFC=") + strlen(buf); + cmd = atcmd_fill("AT+CCFC=", atcmd_len, + &usock_cmd_cb, gu, gph->id, NULL); + if (!cmd) + return -ENOMEM; + sprintf(cmd->buf, "AT+CCFC=%s", buf); + break; + case GSMD_VOICECALL_FWD_ERAS: + if(len < sizeof(*gph) + sizeof(int)) + return -EINVAL; + + reason = (int *) ((void *)gph + sizeof(*gph)); + + sprintf(buf, "%d,4", *reason); + + atcmd_len = 1 + strlen("AT+CCFC=") + strlen(buf); + cmd = atcmd_fill("AT+CCFC=", atcmd_len, + &usock_cmd_cb, gu, gph->id, NULL); + if (!cmd) + return -ENOMEM; + sprintf(cmd->buf, "AT+CCFC=%s", buf); + break; default: return -EINVAL; } diff --git a/src/libgsmd/libgsmd_voicecall.c b/src/libgsmd/libgsmd_voicecall.c index 8804cce..c40ea51 100644 --- a/src/libgsmd/libgsmd_voicecall.c +++ b/src/libgsmd/libgsmd_voicecall.c @@ -125,3 +125,126 @@ int lgsm_voice_ctrl(struct lgsm_handle *lh, const struct lgsm_voicecall_ctrl *ct return 0; } + +int lgsm_voice_fwd_disable(struct lgsm_handle *lh, + enum lgsmd_voicecall_fwd_reason reason) +{ + struct gsmd_msg_hdr *gmh; + int rc; + + gmh = lgsm_gmh_fill(GSMD_MSG_VOICECALL, + GSMD_VOICECALL_FWD_DIS, sizeof(int)); + if (!gmh) + return -ENOMEM; + + *(int *) gmh->data = reason; + + rc = lgsm_send(lh, gmh); + if (rc < gmh->len + sizeof(*gmh)) { + lgsm_gmh_free(gmh);; + return -EIO; + } + + lgsm_gmh_free(gmh); + + return 0; +} + +int lgsm_voice_fwd_enable(struct lgsm_handle *lh, + enum lgsmd_voicecall_fwd_reason reason) +{ + struct gsmd_msg_hdr *gmh; + int rc; + + gmh = lgsm_gmh_fill(GSMD_MSG_VOICECALL, + GSMD_VOICECALL_FWD_EN, sizeof(int)); + if (!gmh) + return -ENOMEM; + + *(int *) gmh->data = reason; + + rc = lgsm_send(lh, gmh); + if (rc < gmh->len + sizeof(*gmh)) { + lgsm_gmh_free(gmh);; + return -EIO; + } + + lgsm_gmh_free(gmh); + + return 0; +} + +int lgsm_voice_fwd_stat(struct lgsm_handle *lh, + enum lgsmd_voicecall_fwd_reason reason) +{ + struct gsmd_msg_hdr *gmh; + int rc; + + gmh = lgsm_gmh_fill(GSMD_MSG_VOICECALL, + GSMD_VOICECALL_FWD_STAT, sizeof(int)); + if (!gmh) + return -ENOMEM; + + *(int *) gmh->data = reason; + + rc = lgsm_send(lh, gmh); + if (rc < gmh->len + sizeof(*gmh)) { + lgsm_gmh_free(gmh);; + return -EIO; + } + + lgsm_gmh_free(gmh); + + return 0; +} + +int lgsm_voice_fwd_reg(struct lgsm_handle *lh, + struct lgsm_voicecall_fwd_reg *fwd_reg) +{ + struct gsmd_msg_hdr *gmh; + struct gsmd_call_fwd_reg *gcfr; + int rc; + + gmh = lgsm_gmh_fill(GSMD_MSG_VOICECALL, + GSMD_VOICECALL_FWD_REG, sizeof(struct gsmd_call_fwd_reg)); + if (!gmh) + return -ENOMEM; + + gcfr = (struct gsmd_call_fwd_reg *)gmh->data; + gcfr->reason = fwd_reg->reason; + strcpy(gcfr->addr.number, fwd_reg->number.addr); + + rc = lgsm_send(lh, gmh); + if (rc < gmh->len + sizeof(*gmh)) { + lgsm_gmh_free(gmh);; + return -EIO; + } + + lgsm_gmh_free(gmh); + + return 0; +} + +int lgsm_voice_fwd_erase(struct lgsm_handle *lh, + enum lgsmd_voicecall_fwd_reason reason) +{ + struct gsmd_msg_hdr *gmh; + int rc; + + gmh = lgsm_gmh_fill(GSMD_MSG_VOICECALL, + GSMD_VOICECALL_FWD_ERAS, sizeof(int)); + if (!gmh) + return -ENOMEM; + + *(int *) gmh->data = reason; + + 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/shell.c b/src/util/shell.c index a8109df..976acc2 100644 --- a/src/util/shell.c +++ b/src/util/shell.c @@ -413,6 +413,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; + struct gsmd_call_fwd_stat *gcfs; int *ret; switch (gmh->msg_subtype) { @@ -440,6 +441,26 @@ static int call_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh) (*ret)? printf("+CME ERROR %d\n", *ret) : printf("OK\n"); pending_responses --; break; + case GSMD_VOICECALL_FWD_DIS: + pending_responses --; + break; + case GSMD_VOICECALL_FWD_EN: + pending_responses --; + break; + case GSMD_VOICECALL_FWD_STAT: + gcfs = (struct gsmd_call_fwd_stat*) ((char *)gmh + sizeof(*gmh)); + + printf("+CCFC:%d, %d, %s\n",gcfs->status, gcfs->classx, gcfs->addr.number); + + if (gcfs->is_last) + pending_responses --; + break; + case GSMD_VOICECALL_FWD_REG: + pending_responses --; + break; + case GSMD_VOICECALL_FWD_ERAS: + pending_responses --; + break; default: return -EINVAL; } @@ -508,6 +529,11 @@ static int shell_help(void) "\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" + "\tCFD\tDisable call forwarding (CFD=reason)\n" + "\tCFE\tEnable call forwarding (CFE=reason)\n" + "\tCFQ\tQuery the status of call forwarding (CFQ=reason)\n" + "\tCFR\tRegister call forwarding (CFR=reason,number)\n" + "\tCFe\tErase a record of call forwarding (CFe=reason)\n" "\tq\tQuit\n" ); } @@ -862,7 +888,36 @@ int shell_main(struct lgsm_handle *lgsmh, int sync) printf("Add a held call to the conversation\n"); lgsm_voice_ctrl(lgsmh, &ctrl); pending_responses ++; - } else { + } else if ( !strncmp(buf, "CFD", 3)) { + printf("Disable call forwarding\n"); + ptr = strchr(buf, '='); + lgsm_voice_fwd_disable(lgsmh, atoi(ptr+1)); + pending_responses ++; + }else if ( !strncmp(buf, "CFE", 3)) { + printf("Enable call forwarding\n"); + ptr = strchr(buf, '='); + lgsm_voice_fwd_enable(lgsmh, atoi(ptr+1)); + pending_responses ++; + }else if ( !strncmp(buf, "CFQ", 3)) { + printf("Query the status of call forwarding\n"); + ptr = strchr(buf, '='); + lgsm_voice_fwd_stat(lgsmh, atoi(ptr+1)); + pending_responses ++; + }else if ( !strncmp(buf, "CFR", 3)) { + struct lgsm_voicecall_fwd_reg lvfr; + printf("Register call forwarding\n"); + ptr = strchr(buf, '='); + lvfr.reason = atoi(ptr+1); + ptr = strchr(buf, ','); + strcpy(lvfr.number.addr, ptr+1); + lgsm_voice_fwd_reg(lgsmh, &lvfr); + pending_responses ++; + }else if ( !strncmp(buf, "CFe", 3)) { + printf("Erase a record of call forwarding\n"); + ptr = strchr(buf, '='); + lgsm_voice_fwd_erase(lgsmh, atoi(ptr+1)); + pending_responses ++; + }else { printf("Unknown command `%s'\n", buf); } } |