summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/gsmd/usock.h30
-rw-r--r--include/libgsmd/phonebook.h6
-rw-r--r--include/libgsmd/voicecall.h27
-rw-r--r--src/gsmd/ext_response.c18
-rw-r--r--src/gsmd/gsmd.c2
-rw-r--r--src/gsmd/unsolicited.c64
-rw-r--r--src/gsmd/usock.c56
-rw-r--r--src/libgsmd/libgsmd_voicecall.c25
-rw-r--r--src/util/event.c7
-rw-r--r--src/util/shell.c69
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 ) {
+ /*
+ * <number>,<type>,<class>,[<alpha>][,<CLI validity>]
+ */
+
+ 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 ) {
+ /*
+ * <number>,<type>,<class>,[<alpha>][,<CLI validity>]
+ */
+
+ 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: <err> */
+ 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);
}
personal git repositories of Harald Welte. Your mileage may vary