Hi Jonas, thank you, ok to all your comments, except GemaltoVtsQuotes, about which I have commented on patch 2/2.
This atom is obviously heavily inspired from the one of atmodem, hence the double copyright in the header. I assumed that the code was ok there, but a refresh is in order. I noticed another g_try_new and removed it, along with the control on the pointer, but the ones you mentioned in your review escaped my check. I will change the code and re-submit. Regards, Giacinto On Sun, Oct 14, 2018 at 6:48 PM Jonas Bonn <[email protected]> wrote: > > Hi Giacinto, > > Some, mostly trivial, comments below. > > /Jonas > > > On 14/10/18 16:17, Giacinto Cifelli wrote: > > Added voicecall atom specific for Gemalto modems. > > This atom uses the URC ^SLCC to monitor the call status, as well as > > incoming calls. > > --- > > Makefile.am | 3 +- > > drivers/gemaltomodem/gemaltomodem.c | 2 + > > drivers/gemaltomodem/gemaltomodem.h | 2 + > > drivers/gemaltomodem/voicecall.c | 751 ++++++++++++++++++++++++++++ > > 4 files changed, 757 insertions(+), 1 deletion(-) > > create mode 100644 drivers/gemaltomodem/voicecall.c > > > > diff --git a/Makefile.am b/Makefile.am > > index 6667524f..e8e4ed95 100644 > > --- a/Makefile.am > > +++ b/Makefile.am > > @@ -397,7 +397,8 @@ builtin_modules += gemaltomodem > > builtin_sources += drivers/atmodem/atutil.h \ > > drivers/gemaltomodem/gemaltomodem.h \ > > drivers/gemaltomodem/gemaltomodem.c \ > > - drivers/gemaltomodem/location-reporting.c > > + drivers/gemaltomodem/location-reporting.c \ > > + drivers/gemaltomodem/voicecall.c > > > > builtin_modules += xmm7modem > > builtin_sources += drivers/atmodem/atutil.h \ > > diff --git a/drivers/gemaltomodem/gemaltomodem.c > > b/drivers/gemaltomodem/gemaltomodem.c > > index 91cf238a..792c2a1e 100644 > > --- a/drivers/gemaltomodem/gemaltomodem.c > > +++ b/drivers/gemaltomodem/gemaltomodem.c > > @@ -35,6 +35,7 @@ > > static int gemaltomodem_init(void) > > { > > gemalto_location_reporting_init(); > > + gemalto_voicecall_init(); > > > > return 0; > > } > > @@ -42,6 +43,7 @@ static int gemaltomodem_init(void) > > static void gemaltomodem_exit(void) > > { > > gemalto_location_reporting_exit(); > > + gemalto_voicecall_exit(); > > } > > init and exit functions usually reverse the order of the functions so > that last initialized is first to be cleaned up. Presumably not > relevant here but it's good form. > > > > > OFONO_PLUGIN_DEFINE(gemaltomodem, "Gemalto modem driver", VERSION, > > diff --git a/drivers/gemaltomodem/gemaltomodem.h > > b/drivers/gemaltomodem/gemaltomodem.h > > index 7ea1e8fb..43d080a3 100644 > > --- a/drivers/gemaltomodem/gemaltomodem.h > > +++ b/drivers/gemaltomodem/gemaltomodem.h > > @@ -23,3 +23,5 @@ > > > > extern void gemalto_location_reporting_init(); > > extern void gemalto_location_reporting_exit(); > > +extern void gemalto_voicecall_init(); > > +extern void gemalto_voicecall_exit(); > > diff --git a/drivers/gemaltomodem/voicecall.c > > b/drivers/gemaltomodem/voicecall.c > > new file mode 100644 > > index 00000000..c0fee1e4 > > --- /dev/null > > +++ b/drivers/gemaltomodem/voicecall.c > > @@ -0,0 +1,751 @@ > > +/* > > + * > > + * oFono - Open Source Telephony > > + * > > + * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. > > + * Copyright (C) 2018 Gemalto M2M > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License version 2 as > > + * published by the Free Software Foundation. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public License > > + * along with this program; if not, write to the Free Software > > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 > > USA > > + * > > + */ > > + > > +#ifdef HAVE_CONFIG_H > > +#include <config.h> > > +#endif > > + > > +#define _GNU_SOURCE > > +#include <string.h> > > +#include <stdlib.h> > > +#include <stdio.h> > > +#include <errno.h> > > + > > +#include <glib.h> > > + > > +#include <ofono/log.h> > > +#include <ofono/modem.h> > > +#include <ofono/voicecall.h> > > + > > +#include "gatchat.h" > > +#include "gatresult.h" > > + > > +#include "common.h" > > + > > +#include "gemaltomodem.h" > > + > > +static const char *clcc_prefix[] = { "+CLCC:", NULL }; > > +static const char *none_prefix[] = { NULL }; > > + > > +/* According to 27.007 COLP is an intermediate status for ATD */ > > +static const char *atd_prefix[] = { "+COLP:", NULL }; > > + > > +#define FLAG_NEED_CLIP 1 > > + > > +struct voicecall_data { > > + GAtChat *chat; > > + GSList *calls; > > + unsigned int local_release; > > + unsigned int vendor; > > + unsigned char flags; > > + GSList *new_calls; > > +}; > > + > > +struct release_id_req { > > + struct ofono_voicecall *vc; > > + ofono_voicecall_cb_t cb; > > + void *data; > > + int id; > > +}; > > + > > +struct change_state_req { > > + struct ofono_voicecall *vc; > > + ofono_voicecall_cb_t cb; > > + void *data; > > + int affected_types; > > +}; > > + > > +static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data) > > +{ > > + struct change_state_req *req = user_data; > > + struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); > > + struct ofono_error error; > > + > > + decode_at_error(&error, g_at_result_final_response(result)); > > + > > + if (ok && req->affected_types) { > > + GSList *l; > > + struct ofono_call *call; > > + > > + for (l = vd->calls; l; l = l->next) { > > + call = l->data; > > + > > + if (req->affected_types & (1 << call->status)) > > + vd->local_release |= (1 << call->id); > > + } > > + } > > + > > + req->cb(&error, req->data); > > +} > > + > > +static void gemalto_template(const char *cmd, struct ofono_voicecall *vc, > > gemalto_template is not a great name. gemalto_common, > gemalto_call_common...??? > > > + GAtResultFunc result_cb, unsigned int affected_types, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + struct change_state_req *req = g_try_new0(struct change_state_req, 1); > > g_new0 is sufficient. > > > + > > + if (req == NULL) > > + goto error; > > Linux will never fail the allocation so skip the error check. > > > + > > + req->vc = vc; > > + req->cb = cb; > > + req->data = data; > > + req->affected_types = affected_types; > > + > > + if (g_at_chat_send(vd->chat, cmd, none_prefix, > > + result_cb, req, g_free) > 0) > > + return; > > + > > +error: > > + g_free(req); > > + > > + CALLBACK_WITH_FAILURE(cb, data); > > +} > > + > > +static void gemalto_answer(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + gemalto_template("ATA", vc, generic_cb, 0, cb, data); > > +} > > + > > +static void gemalto_hangup_all(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + unsigned int affected = (1 << CALL_STATUS_INCOMING) | > > + (1 << CALL_STATUS_DIALING) | > > + (1 << CALL_STATUS_ALERTING) | > > + (1 << CALL_STATUS_WAITING) | > > + (1 << CALL_STATUS_HELD) | > > + (1 << CALL_STATUS_ACTIVE); > > + > > + /* Hangup all calls */ > > + gemalto_template("AT+CHUP", vc, generic_cb, affected, cb, data); > > +} > > + > > +static void gemalto_hangup(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + unsigned int affected = (1 << CALL_STATUS_ACTIVE); > > + > > + /* Hangup current active call */ > > + gemalto_template("AT+CHLD=1", vc, generic_cb, affected, cb, data); > > +} > > + > > +static void gemalto_hold_all_active(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + unsigned int affected = (1 << CALL_STATUS_ACTIVE); > > + gemalto_template("AT+CHLD=2", vc, generic_cb, affected, cb, data); > > +} > > + > > +static void gemalto_release_all_held(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + unsigned int affected = (1 << CALL_STATUS_INCOMING) | > > + (1 << CALL_STATUS_WAITING); > > + > > + gemalto_template("AT+CHLD=0", vc, generic_cb, affected, cb, data); > > +} > > + > > +static void gemalto_set_udub(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + unsigned int affected = (1 << CALL_STATUS_INCOMING) | > > + (1 << CALL_STATUS_WAITING); > > + > > + gemalto_template("AT+CHLD=0", vc, generic_cb, affected, cb, data); > > +} > > + > > +static void gemalto_release_all_active(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + unsigned int affected = (1 << CALL_STATUS_ACTIVE); > > + > > + gemalto_template("AT+CHLD=1", vc, generic_cb, affected, cb, data); > > +} > > + > > +static void release_id_cb(gboolean ok, GAtResult *result, > > + gpointer user_data) > > +{ > > + struct release_id_req *req = user_data; > > + struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); > > + struct ofono_error error; > > + > > + decode_at_error(&error, g_at_result_final_response(result)); > > + > > + if (ok) > > + vd->local_release = 1 << req->id; > > + > > + req->cb(&error, req->data); > > +} > > + > > +static void gemalto_release_specific(struct ofono_voicecall *vc, int id, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + struct release_id_req *req = g_try_new0(struct release_id_req, 1); > > + char buf[32]; > > + > > + if (req == NULL) > > + goto error; > > + > > + req->vc = vc; > > + req->cb = cb; > > + req->data = data; > > + req->id = id; > > + > > + snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id); > > + > > + if (g_at_chat_send(vd->chat, buf, none_prefix, > > + release_id_cb, req, g_free) > 0) > > + return; > > + > > +error: > > + g_free(req); > > + > > + CALLBACK_WITH_FAILURE(cb, data); > > +} > > + > > +static void gemalto_private_chat(struct ofono_voicecall *vc, int id, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + char buf[32]; > > + > > + snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id); > > + gemalto_template(buf, vc, generic_cb, 0, cb, data); > > +} > > + > > +static void gemalto_create_multiparty(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + gemalto_template("AT+CHLD=3", vc, generic_cb, 0, cb, data); > > +} > > + > > +static void gemalto_transfer(struct ofono_voicecall *vc, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + /* Held & Active */ > > + unsigned int affected = (1 << CALL_STATUS_ACTIVE) | > > + (1 << CALL_STATUS_HELD); > > + > > + /* Transfer can puts held & active calls together and disconnects > > + * from both. However, some networks support transferring of > > + * dialing/ringing calls as well. > > + */ > > + affected |= (1 << CALL_STATUS_DIALING) | > > + (1 << CALL_STATUS_ALERTING); > > + > > + gemalto_template("AT+CHLD=4", vc, generic_cb, affected, cb, data); > > +} > > + > > +static void gemalto_send_dtmf(struct ofono_voicecall *vc, const char *dtmf, > > + ofono_voicecall_cb_t cb, void *data) > > +{ > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + int len = strlen(dtmf); > > + int s; > > + int i; > > + char *buf; > > + struct ofono_modem *modem = ofono_voicecall_get_modem(vc); > > + int use_quotes = ofono_modem_get_integer(modem, "GemaltoVtsQuotes"); > GemaltoVtsQuotes is set unconditionally in patch 2/2. Was that your > intention? If so, you don't need this variable... > > > + > > + /* strlen("+VTS=\"T\";") = 9 + initial AT + null */ > > + buf = g_try_new(char, len * 9 + 3); > > g_new > > > + > > + if (buf == NULL) { > > + CALLBACK_WITH_FAILURE(cb, data); > > + return; > > + } > > Linux won't fail the allocation. > > > + > > + if (use_quotes) > > + s = sprintf(buf, "AT+VTS=\"%c\"", dtmf[0]); > > + else > > + s = sprintf(buf, "AT+VTS=%c", dtmf[0]); > > + > > As per above, use_quotes is always 1... > > > + for (i = 1; i < len; i++) { > > + > > + if (use_quotes) > > + s += sprintf(buf + s, ";+VTS=\"%c\"", dtmf[i]); > > + else > > + s += sprintf(buf + s, ";+VTS=%c", dtmf[i]); > > + } > > + > > + g_at_chat_send(vd->chat, buf, NULL, NULL, NULL, NULL); > > + g_free(buf); > > +} > > + > > +static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, > > + int direction, int status, > > + const char *num, int num_type, int > > clip) > > +{ > > + struct voicecall_data *d = ofono_voicecall_get_data(vc); > > + struct ofono_call *call; > > + > > + /* Generate a call structure for the waiting call */ > > + call = g_try_new(struct ofono_call, 1); > > + if (call == NULL) > > + return NULL; > > g_new... won't fail. > > > + > > + ofono_call_init(call); > > + > > + call->id = ofono_voicecall_get_next_callid(vc); > > + call->type = type; > > + call->direction = direction; > > + call->status = status; > > + > > + if (clip != 2) { > > + strncpy(call->phone_number.number, num, > > + OFONO_MAX_PHONE_NUMBER_LENGTH); > > + call->phone_number.type = num_type; > > + } > > + > > + call->clip_validity = clip; > > + call->cnap_validity = CNAP_VALIDITY_NOT_AVAILABLE; > > + > > + d->calls = g_slist_insert_sorted(d->calls, call, > > at_util_call_compare); > > + > > + return call; > > +} > > + > > +static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data) > > +{ > > + struct cb_data *cbd = user_data; > > + struct ofono_voicecall *vc = cbd->user; > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + ofono_voicecall_cb_t cb = cbd->cb; > > + GAtResultIter iter; > > + const char *num; > > + int type = 128; > > + int validity = 2; > > + struct ofono_error error; > > + struct ofono_call *call; > > + GSList *l; > > + > > + decode_at_error(&error, g_at_result_final_response(result)); > > + > > + if (!ok) > > + goto out; > > + > > + /* On a success, make sure to put all active calls on hold */ > > + for (l = vd->calls; l; l = l->next) { > > + call = l->data; > > + > > + if (call->status != CALL_STATUS_ACTIVE) > > + continue; > > + > > + call->status = CALL_STATUS_HELD; > > + ofono_voicecall_notify(vc, call); > > + } > > + > > + g_at_result_iter_init(&iter, result); > > + > > + if (g_at_result_iter_next(&iter, "+COLP:")) { > > + g_at_result_iter_next_string(&iter, &num); > > + g_at_result_iter_next_number(&iter, &type); > > + > > + if (strlen(num) > 0) > > + validity = 0; > > + else > > + validity = 2; > > + > > + DBG("colp_notify: %s %d %d", num, type, validity); > > + } > > + > > + /* Generate a voice call that was just dialed, we guess the ID */ > > + call = create_call(vc, 0, 0, CALL_STATUS_DIALING, num, type, > > validity); > > + if (call == NULL) { > > + ofono_error("Unable to malloc, call tracking will fail!"); > > + return; > > + } > > + > > + /* oFono core will generate a call with the dialed number > > + * inside its dial callback. Unless we got COLP information > > + * we do not need to communicate that a call is being > > + * dialed > > + */ > > + if (validity != 2) > > + ofono_voicecall_notify(vc, call); > > + > > +out: > > + cb(&error, cbd->data); > > +} > > + > > +static void gemalto_dial(struct ofono_voicecall *vc, > > + const struct ofono_phone_number *ph, > > + enum ofono_clir_option clir, ofono_voicecall_cb_t cb, > > + void *data) > > +{ > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + struct cb_data *cbd = cb_data_new(cb, data); > > + char buf[256]; > > + > > + cbd->user = vc; > > + > > + if (ph->type == 145) > > + snprintf(buf, sizeof(buf), "ATD+%s", ph->number); > > + else > > + snprintf(buf, sizeof(buf), "ATD%s", ph->number); > > > + > > + switch (clir) { > > + case OFONO_CLIR_OPTION_INVOCATION: > > + strcat(buf, "I"); > > I'm a bit allergic to snprintf followed by strcat... you already know > the length of buf (at least, if you grab the return value from > snprintf), so use snprintf again and save having to do a search for \0 > like strcat does. > > > + break; > > + case OFONO_CLIR_OPTION_SUPPRESSION: > > + strcat(buf, "i"); > > + break; > > + default: > > + break; > > + } > > + > > + strcat(buf, ";"); > > + > > + if (g_at_chat_send(vd->chat, buf, atd_prefix, > > + atd_cb, cbd, g_free) > 0) > > + return; > > + > > + g_free(cbd); > > + > > + CALLBACK_WITH_FAILURE(cb, data); > > +} > > + > > +static gboolean gemalto_parse_slcc(GAtResult *result, GSList **l, > > + unsigned int *ret_mpty_ids) > > +{ > > + GAtResultIter iter; > > + int id, dir, status, type; > > + ofono_bool_t mpty; > > + struct ofono_call *call; > > + unsigned int mpty_ids = 0; > > + gboolean last = TRUE; > > + const char *str = ""; > > + int number_type = 129; > > + > > + g_at_result_iter_init(&iter, result); > > + > > + g_at_result_iter_next(&iter, "^SLCC:"); > > + > > + if (!g_at_result_iter_next_number(&iter, &id)) > > + goto finish; > > + > > + last = FALSE; > > + > > + if (id == 0) > > + goto finish; > > + > > + if (!g_at_result_iter_next_number(&iter, &dir)) > > + goto finish; > > + > > + if (!g_at_result_iter_next_number(&iter, &status)) > > + goto finish; > > + > > + if (status > 5) > > + goto finish; > > + > > + if (!g_at_result_iter_next_number(&iter, &type)) > > + goto finish; > > + > > + if (!g_at_result_iter_next_number(&iter, &mpty)) > > + goto finish; > > + > > + /* skip 'Reserved=0' parameter, only difference from CLCC */ > > + if (!g_at_result_iter_skip_next(&iter)) > > + goto finish; > > + > > + if (g_at_result_iter_next_string(&iter, &str)) > > + g_at_result_iter_next_number(&iter, &number_type); > > + > > + call = g_new0(struct ofono_call, 1); > > + ofono_call_init(call); > > + call->id = id; > > + call->direction = dir; > > + call->status = status; > > + call->type = type; > > + strncpy(call->phone_number.number, str, > > + OFONO_MAX_PHONE_NUMBER_LENGTH); > > + call->phone_number.type = number_type; > > + > > + if (strlen(call->phone_number.number) > 0) > > + call->clip_validity = 0; > > + else > > + call->clip_validity = 2; > > + > > + *l = g_slist_insert_sorted(*l, call, at_util_call_compare); > > + > > + if (mpty) > > + mpty_ids |= 1 << id; > > + > > + if (ret_mpty_ids) > > + *ret_mpty_ids = mpty_ids; > > + > > +finish: > > + return last; > > +} > > + > > +static void clcc_cb(gboolean ok, GAtResult *result, gpointer user_data) > > +{ > > + struct ofono_voicecall *vc = user_data; > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + GSList *l; > > + > > + if (!ok) > > + return; > > + > > + vd->calls = at_util_parse_clcc(result, NULL); > > + > > + for (l = vd->calls; l; l = l->next) > > + ofono_voicecall_notify(vc, l->data); > > +} > > + > > +/* > > + * ^SLCC, except for one RFU parameter (see above in the parsing), is > > identical > > + * to +CLCC, but as URC it is parsed line by line, and the last line is > > + * indicated by an empty "^SLCC:" (equivalent to the "OK" for CLCC). > > + */ > > +static void slcc_notify(GAtResult *result, gpointer user_data) > > +{ > > + struct ofono_voicecall *vc = user_data; > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + GSList *n, *o; > > + struct ofono_call *nc, *oc; > > + > > + gboolean last = gemalto_parse_slcc(result, &vd->new_calls, NULL); > > + > > + if (!last) > > + return; > > + > > + n = vd->new_calls; > > + o = vd->calls; > > + > > + while (n || o) { > > + > > + nc = n ? n->data : NULL; > > + oc = o ? o->data : NULL; > > + > > + if (oc && (nc == NULL || (nc->id > oc->id))) { > > + enum ofono_disconnect_reason reason; > > + > > + if (vd->local_release & (1 << oc->id)) > > + reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP; > > + else > > + reason = > > OFONO_DISCONNECT_REASON_REMOTE_HANGUP; > > + > > + if (!oc->type) > > + ofono_voicecall_disconnected(vc, oc->id, > > + reason, NULL); > > + > > + o = o->next; > > + } else if (nc && (oc == NULL || (nc->id < oc->id))) { > > + > > + if (nc->type == 0) /* new call, signal it */ > > + ofono_voicecall_notify(vc, nc); > > + > > + n = n->next; > > + } else { > > + /* > > + * Always use the clip_validity from old call > > + * the only place this is truly told to us is > > + * in the CLIP notify, the rest are fudged > > + * anyway. Useful when RING, CLIP is used, > > + * and we're forced to use CLCC/SLCC and clip_validity > > + * is 1 > > + */ > > + if (oc->clip_validity == 1) > > + nc->clip_validity = oc->clip_validity; > > + > > + /* > > + * CNAP doesn't arrive as part of CLCC, always > > + * re-use from the old call > > + */ > > + strncpy(nc->name, oc->name, > > + OFONO_MAX_CALLER_NAME_LENGTH); > > + nc->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0'; > > + nc->cnap_validity = oc->cnap_validity; > > + > > + /* > > + * CDIP doesn't arrive as part of CLCC, always > > + * re-use from the old call > > + */ > > + memcpy(&nc->called_number, &oc->called_number, > > + sizeof(oc->called_number)); > > + > > + /* > > + * If the CLIP is not provided and the CLIP never > > + * arrives, or RING is used, then signal the call > > + * here > > + */ > > + if (nc->status == CALL_STATUS_INCOMING && > > + (vd->flags & FLAG_NEED_CLIP)) { > > + if (nc->type == 0) > > + ofono_voicecall_notify(vc, nc); > > + > > + vd->flags &= ~FLAG_NEED_CLIP; > > + } else if (memcmp(nc, oc, sizeof(*nc)) && nc->type == > > 0) > > + ofono_voicecall_notify(vc, nc); > > + > > + n = n->next; > > + o = o->next; > > + } > > + } > > + > > + g_slist_free_full(vd->calls, g_free); > > + > > + vd->calls = vd->new_calls; > > + vd->new_calls = NULL; > > + > > + vd->local_release = 0; > > +} > > + > > +static void cssi_notify(GAtResult *result, gpointer user_data) > > +{ > > + struct ofono_voicecall *vc = user_data; > > + GAtResultIter iter; > > + int code, index; > > + > > + g_at_result_iter_init(&iter, result); > > + > > + if (!g_at_result_iter_next(&iter, "+CSSI:")) > > + return; > > + > > + if (!g_at_result_iter_next_number(&iter, &code)) > > + return; > > + > > + if (!g_at_result_iter_next_number(&iter, &index)) > > + index = 0; > > + > > + ofono_voicecall_ssn_mo_notify(vc, 0, code, index); > > +} > > + > > +static void cssu_notify(GAtResult *result, gpointer user_data) > > +{ > > + struct ofono_voicecall *vc = user_data; > > + GAtResultIter iter; > > + int code; > > + int index; > > + const char *num; > > + struct ofono_phone_number ph; > > + > > + ph.number[0] = '\0'; > > + ph.type = 129; > > + > > + g_at_result_iter_init(&iter, result); > > + > > + if (!g_at_result_iter_next(&iter, "+CSSU:")) > > + return; > > + > > + if (!g_at_result_iter_next_number(&iter, &code)) > > + return; > > + > > + if (!g_at_result_iter_next_number_default(&iter, -1, &index)) > > + goto out; > > + > > + if (!g_at_result_iter_next_string(&iter, &num)) > > + goto out; > > + > > + strncpy(ph.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); > > + > > + if (!g_at_result_iter_next_number(&iter, &ph.type)) > > + return; > > + > > +out: > > + ofono_voicecall_ssn_mt_notify(vc, 0, code, index, &ph); > > +} > > + > > +static void gemalto_voicecall_initialized(gboolean ok, GAtResult *result, > > + gpointer user_data) > > +{ > > + struct ofono_voicecall *vc = user_data; > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + > > + DBG("voicecall_init: registering to notifications"); > > + > > + /* NO CARRIER, NO ANSWER, BUSY, NO DIALTONE are handled through SLCC > > */ > > + g_at_chat_register(vd->chat, "^SLCC:", slcc_notify, FALSE, vc, NULL); > > + g_at_chat_register(vd->chat, "+CSSI:", cssi_notify, FALSE, vc, NULL); > > + g_at_chat_register(vd->chat, "+CSSU:", cssu_notify, FALSE, vc, NULL); > > + > > + ofono_voicecall_register(vc); > > + > > + /* Populate the call list */ > > + g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_cb, vc, NULL); > > +} > > + > > +static int gemalto_voicecall_probe(struct ofono_voicecall *vc, > > + unsigned int vendor, void *data) > > +{ > > + GAtChat *chat = data; > > + struct voicecall_data *vd; > > + > > + vd = g_try_new0(struct voicecall_data, 1); > > + > > + if (vd == NULL) > > + return -ENOMEM; > > + > > + vd->chat = g_at_chat_clone(chat); > > + vd->vendor = vendor; > > + ofono_voicecall_set_data(vc, vd); > > + g_at_chat_send(vd->chat, "AT+CSSN=1,1", NULL, NULL, NULL, NULL); > > + g_at_chat_send(vd->chat, "AT^SLCC=1", NULL, > > + gemalto_voicecall_initialized, vc, NULL); > > + return 0; > > +} > > + > > +static void gemalto_voicecall_remove(struct ofono_voicecall *vc) > > +{ > > + struct voicecall_data *vd = ofono_voicecall_get_data(vc); > > + > > + ofono_voicecall_set_data(vc, NULL); > > + > > + g_at_chat_unref(vd->chat); > > + g_free(vd); > > +} > > + > > +static struct ofono_voicecall_driver driver = { > > + .name = "gemaltomodem", > > + .probe = gemalto_voicecall_probe, > > + .remove = gemalto_voicecall_remove, > > + .dial = gemalto_dial, > > + .answer = gemalto_answer, > > + .hangup_all = gemalto_hangup_all, > > + .hangup_active = gemalto_hangup, > > + .hold_all_active = gemalto_hold_all_active, > > + .release_all_held = gemalto_release_all_held, > > + .set_udub = gemalto_set_udub, > > + .release_all_active = gemalto_release_all_active, > > + .release_specific = gemalto_release_specific, > > + .private_chat = gemalto_private_chat, > > + .create_multiparty = gemalto_create_multiparty, > > + .transfer = gemalto_transfer, > > + .deflect = NULL, > > + .swap_without_accept = NULL, > > + .send_tones = gemalto_send_dtmf > > +}; > > + > > +void gemalto_voicecall_init(void) > > +{ > > + ofono_voicecall_driver_register(&driver); > > +} > > + > > +void gemalto_voicecall_exit(void) > > +{ > > + ofono_voicecall_driver_unregister(&driver); > > +} > _______________________________________________ ofono mailing list [email protected] https://lists.ofono.org/mailman/listinfo/ofono
