summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/gsmd/usock.h38
-rw-r--r--include/libgsmd/voicecall.h35
-rw-r--r--src/gsmd/usock.c148
-rw-r--r--src/libgsmd/libgsmd_voicecall.c123
-rw-r--r--src/util/shell.c57
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);
}
}
personal git repositories of Harald Welte. Your mileage may vary