--- Makefile.am | 3 +- drivers/samsungipcmodem/samsungipcmodem.c | 2 + drivers/samsungipcmodem/samsungipcmodem.h | 2 + drivers/samsungipcmodem/voicecall.c | 358 +++++++++++++++++++++++++++++ 4 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 drivers/samsungipcmodem/voicecall.c
diff --git a/Makefile.am b/Makefile.am index 32ed3de..012c78d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -190,7 +190,8 @@ builtin_sources += \ drivers/samsungipcmodem/ipc.h \ drivers/samsungipcmodem/devinfo.c \ drivers/samsungipcmodem/sim.c \ - drivers/samsungipcmodem/network-registration.c + drivers/samsungipcmodem/network-registration.c \ + drivers/samsungipcmodem/voicecall.c builtin_cflags += @SAMSUNGIPC_CFLAGS@ builtin_libadd += @SAMSUNGIPC_LIBS@ endif diff --git a/drivers/samsungipcmodem/samsungipcmodem.c b/drivers/samsungipcmodem/samsungipcmodem.c index a192686..72553a5 100644 --- a/drivers/samsungipcmodem/samsungipcmodem.c +++ b/drivers/samsungipcmodem/samsungipcmodem.c @@ -33,11 +33,13 @@ static int samsungipcmodem_init(void) samsungipc_devinfo_init(); samsungipc_sim_init(); samsungipc_netreg_init(); + samsungipc_voicecall_init(); return 0; } static void samsungipcmodem_exit(void) { + samsungipc_voicecall_exit(); samsungipc_netreg_exit(); samsungipc_sim_exit(); samsungipc_devinfo_exit(); diff --git a/drivers/samsungipcmodem/samsungipcmodem.h b/drivers/samsungipcmodem/samsungipcmodem.h index 3f4d5ad..092effd 100644 --- a/drivers/samsungipcmodem/samsungipcmodem.h +++ b/drivers/samsungipcmodem/samsungipcmodem.h @@ -25,3 +25,5 @@ extern void samsungipc_sim_init(void); extern void samsungipc_sim_exit(void); extern void samsungipc_netreg_init(void); extern void samsungipc_netreg_exit(void); +extern void samsungipc_voicecall_init(void); +extern void samsungipc_voicecall_exit(void); diff --git a/drivers/samsungipcmodem/voicecall.c b/drivers/samsungipcmodem/voicecall.c new file mode 100644 index 0000000..5236e0f --- /dev/null +++ b/drivers/samsungipcmodem/voicecall.c @@ -0,0 +1,358 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2012 Simon Busch <morp...@gravedo.de>. All rights reserved. + * + * 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 "samsungipcmodem.h" +#include "ipc.h" +#include "util.h" + +/* Amount of ms we wait between call status requests */ +#define CALL_STATUS_POLL_INTERVAL 500 + +struct voicecall_data { + struct ipc_device *device; + int dtmf_active; + unsigned int status_update_pending; +}; + +static void send_dtmf_cb(uint16_t cmd, void *data, uint16_t length, uint8_t error, void *user_data) +{ + struct cb_data *cbd = user_data; + ofono_voicecall_cb_t cb = cbd->cb; + unsigned char ret; + + if (error) { + CALLBACK_WITH_FAILURE(cb, cbd->data); + goto done; + } + + ret = *((unsigned char*) data); + if (!ret) { + CALLBACK_WITH_FAILURE(cb, cbd->data); + goto done; + } + + CALLBACK_WITH_SUCCESS(cb, cbd->data); + +done: + g_free(cbd); +} + +static void samsungipc_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); + struct cb_data *cbd; + struct ipc_call_cont_dtmf *reqs; + int tone_count; + int n; + + DBG(""); + + tone_count = strlen(dtmf); + + reqs = g_try_new0(struct ipc_call_cont_dtmf, tone_count); + if (!reqs) { + CALLBACK_WITH_FAILURE(cb, data); + return; + } + + cbd = cb_data_new(cb, data); + + for (n = 0; n > tone_count; n++) { + reqs[n].state = IPC_CALL_DTMF_STATE_START; + reqs[n].tone = dtmf[n]; + } + + if(ipc_device_enqueue_message(vd->device, IPC_CALL_RELEASE, IPC_TYPE_EXEC, + reqs, sizeof(struct ipc_call_cont_dtmf) * tone_count, + send_dtmf_cb, cbd) > 0) + return; + + CALLBACK_WITH_FAILURE(cb, data); + g_free(cbd); +} + +static void common_call_cb(uint16_t cmd, void *data, uint16_t length, uint8_t error, void *user_data) +{ + struct cb_data *cbd = user_data; + ofono_voicecall_cb_t cb = cbd->cb; + struct ipc_gen_phone_res *resp = data; + + if (error || ipc_gen_phone_res_check(resp) < 0) { + CALLBACK_WITH_FAILURE(cb, cbd->data); + goto done; + } + + CALLBACK_WITH_SUCCESS(cb, cbd->data); + +done: + g_free(cbd); +} + +static void samsungipc_hangup_active(struct ofono_voicecall *vc, + 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); + + DBG(""); + + if(ipc_device_enqueue_message(vd->device, IPC_CALL_RELEASE, IPC_TYPE_EXEC, + NULL, 0, common_call_cb, cbd) > 0) + return; + + CALLBACK_WITH_FAILURE(cb, data); + g_free(cbd); +} + +static void samsungipc_answer(struct ofono_voicecall *vc, + 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); + + DBG(""); + + if(ipc_device_enqueue_message(vd->device, IPC_CALL_ANSWER, IPC_TYPE_EXEC, + NULL, 0, common_call_cb, cbd) > 0) + return; + + CALLBACK_WITH_FAILURE(cb, data); + g_free(cbd); +} + +static void samsungipc_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 ipc_call_outgoing *req; + struct cb_data *cbd; + + DBG(""); + + req = g_try_new0(struct ipc_call_outgoing, 1); + if (!req) { + CALLBACK_WITH_FAILURE(cb, data); + return; + } + + cbd = cb_data_new(cb, data); + + switch (clir) { + case OFONO_CLIR_OPTION_DEFAULT: + req->identity = IPC_CALL_IDENTITY_DEFAULT; + break; + case OFONO_CLIR_OPTION_INVOCATION: + req->identity = IPC_CALL_IDENTITY_SHOW; + break; + case OFONO_CLIR_OPTION_SUPPRESSION: + req->identity = IPC_CALL_IDENTITY_HIDE; + break; + } + + req->type = IPC_CALL_TYPE_VOICE; + req->prefix = (ph->type == 145) ? IPC_CALL_PREFIX_INTL : IPC_CALL_PREFIX_NONE; + req->length = strlen(ph->number); + strncpy((char*) req->number, ph->number, 86); + + if(ipc_device_enqueue_message(vd->device, IPC_CALL_OUTGOING, IPC_TYPE_EXEC, + req, sizeof(struct ipc_call_outgoing), + common_call_cb, cbd) > 0) + return; + + CALLBACK_WITH_FAILURE(cb, data); + + g_free(cbd); +} + +static void call_list_cb(uint16_t cmd, void *data, uint16_t length, uint8_t error, void *user_data) +{ + struct ofono_voicecall *vc = user_data; + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + struct ipc_message_info resp; + struct ipc_call_list_entry *entry; + struct ofono_call call; + char *entry_number; + int num_entries; + int n; + + DBG(""); + + if (error) + return; + + resp.data = data; + resp.length = length; + + num_entries = ipc_call_list_response_get_num_entries(&resp); + + DBG("Got %i entries", num_entries); + + for (n = 0; n < num_entries; n++) { + ofono_call_init(&call); + + entry = ipc_call_list_response_get_entry(&resp, n); + + call.id = entry->idx; + call.type = (entry->type == IPC_CALL_TYPE_VOICE) ? 0 : 1; + call.direction = (entry->term == IPC_CALL_TERM_MT) ? 0 : 1; + call.status = entry->state - 1; + + memset(&call.phone_number.type, 0, OFONO_MAX_PHONE_NUMBER_LENGTH); + + entry_number = ipc_call_list_response_get_entry_number(&resp, n); + if (entry_number != NULL) { + strncpy(call.phone_number.number, entry_number, OFONO_MAX_CALLER_NAME_LENGTH); + call.phone_number.number[entry->number_len] = '\0'; + call.phone_number.type = (entry->number_len > 0 && call.phone_number.number[0] == '+') ? 145 : 129; + g_free(entry_number); + } + + DBG("id=%i, type=%i, direction=%i, status=%i, number=%s", + call.id, call.type, call.direction, call.status, + call.phone_number.number); + + ofono_voicecall_notify(vc, &call); + } + + vd->status_update_pending = 0; +} + +static void update_call_status(struct ofono_voicecall *vc) +{ + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + + if (vd->status_update_pending) + return; + + vd->status_update_pending = 1; + + if(ipc_device_enqueue_message(vd->device, IPC_CALL_LIST, IPC_TYPE_GET, + NULL, 0, call_list_cb, vc) > 0) + return; +} + +static void notify_call_incoming_cb(uint16_t cmd, void *data, uint16_t length, void *user_data) +{ + struct ofono_voicecall *vc = user_data; + + update_call_status(vc); +} + +static void notify_call_status_cb(uint16_t cmd, void *data, uint16_t length, void *user_data) +{ + struct ofono_voicecall *vc = user_data; + struct ipc_call_status *resp = data; + + DBG(""); + + if (resp->state == IPC_CALL_STATE_RELEASED) { + // FIXME what are possible values for resp->reason? + ofono_voicecall_disconnected(vc, resp->id, OFONO_DISCONNECT_REASON_UNKNOWN, NULL); + return; + } + + update_call_status(vc); +} + +static gboolean initialization_done_cb(gpointer user_data) +{ + struct ofono_voicecall *vc = user_data; + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + + ipc_device_add_notifcation_watch(vd->device, IPC_CALL_INCOMING, notify_call_incoming_cb, vc); + ipc_device_add_notifcation_watch(vd->device, IPC_CALL_STATUS, notify_call_status_cb, vc); + ipc_device_add_notifcation_watch(vd->device, IPC_CALL_RELEASE, notify_call_status_cb, vc); + ipc_device_add_notifcation_watch(vd->device, IPC_CALL_ANSWER, notify_call_status_cb, vc); + ipc_device_add_notifcation_watch(vd->device, IPC_CALL_OUTGOING, notify_call_status_cb, vc); + ipc_device_add_notifcation_watch(vd->device, IPC_CALL_WAITING, notify_call_status_cb, vc); + + ofono_voicecall_register(vc); + + return FALSE; +} + +static int samsungipc_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, + void *data) +{ + struct voicecall_data *vd; + + DBG(""); + + vd = g_try_new0(struct voicecall_data, 1); + if (vd == NULL) + return -ENOMEM; + + vd->device = data; + vd->dtmf_active = 0; + vd->status_update_pending = 0; + + ofono_voicecall_set_data(vc, vd); + + g_timeout_add_seconds(0, initialization_done_cb, vc); + + return 0; +} + +static void samsungipc_voicecall_remove(struct ofono_voicecall *vc) +{ + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + + g_free(vd); +} + +static struct ofono_voicecall_driver driver = { + .name = "samsungipcmodem", + .probe = samsungipc_voicecall_probe, + .remove = samsungipc_voicecall_remove, + .dial = samsungipc_dial, + .answer = samsungipc_answer, + .hangup_active = samsungipc_hangup_active, + .send_tones = samsungipc_send_dtmf, +}; + +void samsungipc_voicecall_init(void) +{ + ofono_voicecall_driver_register(&driver); +} + +void samsungipc_voicecall_exit(void) +{ + ofono_voicecall_driver_unregister(&driver); +} -- 1.7.9.5 _______________________________________________ ofono mailing list ofono@ofono.org http://lists.ofono.org/listinfo/ofono