--- /dev/null
+/*
+ *
+ * tel-plugin-imc-pr3
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/if_ether.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include <tcore.h>
+#include <plugin.h>
+#include <core_object.h>
+
+
+#include <user_request.h>
+#include <hal.h>
+#include <mux.h>
+#include <at.h>
+#include <queue.h>
+
+#include "util_imc.h"
+#include "pr3_audio.h"
+
+#define DEVICE_IFX "/dev/ttyIFX0"
+#define N_GSM0710 21
+#define NUM_DLC 7
+#define BUF_LEN_MAX 512
+
+#define CHANNEL_MODEM 0
+#define CHANNEL_CALL 0
+#define CHANNEL_SIM 1
+#define CHANNEL_SMS 2
+#define CHANNEL_NETWORK 3
+#define CHANNEL_PS 4
+#define CHANNEL_SAT 1
+#define CHANNEL_SS 3
+#define CHANNEL_DATA_1 5
+#define CHANNEL_DATA_2 6
+#define CHANNEL_NONE -1
+
+struct custom_data {
+ GIOChannel *channel;
+ guint watch_id;
+ void *cb;
+ void *cb_data;
+ guint dlc_poll_count;
+ guint dlc_poll_src;
+ gboolean rawip_enabled;
+};
+
+struct gsm_config {
+ unsigned int adaption;
+ unsigned int encapsulation;
+ unsigned int initiator;
+ unsigned int t1;
+ unsigned int t2;
+ unsigned int t3;
+ unsigned int n2;
+ unsigned int mru;
+ unsigned int mtu;
+ unsigned int k;
+ unsigned int i;
+ unsigned int clocal;
+ unsigned int unused[7]; /* Padding for expansion without breaking stuff */
+};
+
+#define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config)
+#define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config)
+
+struct gsm_netconfig {
+ unsigned int adaption; /* Adaption to use in network mode */
+ unsigned short protocol; /* Protocol to use - only ETH_P_IP supported */
+ unsigned short unused2;
+ char if_name[IFNAMSIZ]; /* interface name format string */
+ __u8 unused[28]; /* For future use */
+};
+
+#define GSMIOC_ENABLE_NET _IOW('G', 2, struct gsm_netconfig)
+#define GSMIOC_DISABLE_NET _IO('G', 3)
+
+/* Virtual ttys for the mux */
+static const char *dlc_nodes[NUM_DLC] = { "/dev/gsmtty1", "/dev/gsmtty2",
+ "/dev/gsmtty3", "/dev/gsmtty4",
+ "/dev/gsmtty6", "/dev/gsmtty7",
+ "/dev/gsmtty8" };
+
+/* CMUX structure */
+typedef struct cmux {
+ TcoreHal *hal[NUM_DLC];
+ int channel_nb;
+} MUX;
+
+/* Global pointer MUX Object pointer */
+MUX *g_mux_obj_ptr = NULL;
+
+static TReturn hal_power(TcoreHal *hal, gboolean flag,
+ TcoreHalPowerCallBack func, void *user_data);
+static TReturn hal_send(TcoreHal *hal, unsigned int data_len, void *data);
+static TReturn hal_link_object_channel(CoreObject *object);
+static TReturn hal_setup_pdp(CoreObject *co, TcoreHalSetupPDPCallBack func,
+ void *user_data, unsigned int cid);
+
+static struct tcore_hal_operations hops =
+{
+ .power = hal_power,
+ .send = hal_send,
+ .set_sound_path = pr3_audio_set_sound_path,
+ .link_object_channel = hal_link_object_channel,
+ .setup_pdp = hal_setup_pdp,
+};
+
+static gboolean on_recv_tty_message(GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+ TcoreHal *hal = data;
+ char read_buffer[BUF_LEN_MAX];
+ size_t bytes_read;
+
+ if (condition & G_IO_NVAL)
+ return FALSE;
+
+ bytes_read = channel_read(channel, read_buffer, BUF_LEN_MAX);
+ if (bytes_read == 0) {
+ err("channel_read error.");
+ return FALSE;
+ }
+
+ dbg("bytes_read = %d", bytes_read);
+ tcore_hal_emit_recv_callback(hal, bytes_read, read_buffer);
+
+ tcore_hal_dispatch_response_data(hal, 0, bytes_read, read_buffer);
+
+ return TRUE;
+}
+
+
+static TcoreHal *get_object_hal(const char *obj_name)
+{
+ if (strcmp(obj_name, "modem") == 0)
+ return g_mux_obj_ptr->hal[CHANNEL_MODEM];
+ else if (strcmp(obj_name, "call") == 0)
+ return g_mux_obj_ptr->hal[CHANNEL_CALL];
+ else if (strcmp(obj_name, "sim") == 0)
+ return g_mux_obj_ptr->hal[CHANNEL_SIM];
+ else if (strcmp(obj_name, "umts_sms") == 0)
+ return g_mux_obj_ptr->hal[CHANNEL_SMS];
+ else if (strcmp(obj_name, "umts_network") == 0)
+ return g_mux_obj_ptr->hal[CHANNEL_NETWORK];
+ else if (strcmp(obj_name, "umts_ps") == 0)
+ return g_mux_obj_ptr->hal[CHANNEL_PS];
+ else if (strcmp(obj_name, "sat") == 0)
+ return g_mux_obj_ptr->hal[CHANNEL_SAT];
+ else if (strcmp(obj_name, "ss") == 0)
+ return g_mux_obj_ptr->hal[CHANNEL_SS];
+
+ return NULL;
+}
+
+static gboolean setup_channel(GIOChannel *io)
+{
+ GIOFlags io_flags;
+
+ if (g_io_channel_set_encoding(io, NULL, NULL) != G_IO_STATUS_NORMAL)
+ return FALSE;
+
+ g_io_channel_set_buffered(io, FALSE);
+ io_flags = g_io_channel_get_flags(io);
+ io_flags |= (G_IO_FLAG_NONBLOCK & G_IO_FLAG_SET_MASK);
+
+ if (g_io_channel_set_flags(io, io_flags, NULL) != G_IO_STATUS_NORMAL)
+ return FALSE;
+
+ g_io_channel_set_close_on_unref(io, TRUE);
+
+ return TRUE;
+}
+
+static void set_termio(int fd)
+{
+ struct termios newtio;
+
+ /* Switch TTY to raw mode */
+ memset(&newtio, 0, sizeof(newtio));
+ cfmakeraw(&newtio);
+
+ /* Line is local */
+ newtio.c_cflag |= CLOCAL;
+
+ tcflush(fd, TCIFLUSH);
+ tcsetattr(fd, TCSANOW, &newtio);
+}
+
+static int open_device(const char *tty)
+{
+ int fd;
+
+ fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0)
+ return -1;
+
+ set_termio(fd);
+
+ return fd;
+}
+
+static GIOChannel *tty_open(const char *tty)
+{
+ GIOChannel *channel;
+ int fd;
+
+ fd = open_device(tty);
+ if (fd < 0)
+ return NULL;
+
+ dbg("fd =%d ", fd);
+
+ channel = g_io_channel_unix_new(fd);
+ if (channel == NULL) {
+ close(fd);
+ return NULL;
+ }
+
+ setup_channel(channel);
+
+ return channel;
+}
+
+static guint register_gio_watch(GIOChannel *channel, void *callback, TcoreHal *h)
+{
+ guint source;
+
+ if (channel == NULL || !callback)
+ return 0;
+
+ source = g_io_add_watch(channel, G_IO_IN, (GIOFunc) callback, h);
+
+ return source;
+}
+
+static gboolean dlc_ready_check(gpointer user_data)
+{
+ TcoreHal *h = user_data;
+ struct custom_data *cdata = tcore_hal_ref_user_data(h);
+ struct stat st;
+ TcoreHalPowerCallBack func = (TcoreHalPowerCallBack)cdata->cb;
+ int i;
+ gint fd;
+
+ dbg("Entry");
+
+ cdata->dlc_poll_count++;
+
+ if (stat(dlc_nodes[0], &st) < 0) {
+ /* only possible error is ENOENT */
+ if (cdata->dlc_poll_count > 6)
+ goto error;
+
+ return TRUE;
+ }
+
+ fd = g_io_channel_unix_get_fd(cdata->channel);
+ set_termio(fd);
+
+ /* Allocating memory for mux */
+ g_mux_obj_ptr = (MUX *) g_new0(struct cmux, 1);
+ if (!g_mux_obj_ptr) {
+ err("Failed to allocate memory");
+ goto error;
+ }
+
+ for (i = 0; i < NUM_DLC; i++) {
+ TcoreHal *hal = NULL;
+ struct custom_data *hal_data;
+ char channel_id_name[16];
+
+ hal_data = g_new0(struct custom_data, 1);
+
+ hal_data->channel = tty_open(dlc_nodes[i]);
+
+ if (hal_data->channel == NULL) {
+ err("Failed to open tty port - [%s]", strerror(errno));
+
+ goto error;
+ } else
+ dbg("Virtual tty port opened successfully. channel:%p, path:%s",
+ hal_data->channel, dlc_nodes[i]);
+
+ snprintf(channel_id_name, sizeof(channel_id_name), "channel_%d", i);
+ hal = tcore_hal_new(NULL, channel_id_name, &hops, TCORE_HAL_MODE_AT);
+ hal_data->watch_id = register_gio_watch(hal_data->channel, on_recv_tty_message, hal);
+
+ dbg("hal=%p %s=%p watch_id=%d ", hal, channel_id_name, hal_data->channel, hal_data->watch_id);
+
+ g_io_channel_unref(hal_data->channel);
+
+ // Store CMUX Channel
+ g_mux_obj_ptr->hal[i] = hal;
+ g_mux_obj_ptr->channel_nb++;
+
+ // Set Logical HAL Power State to TRUE
+ tcore_hal_set_power_state(hal, TRUE);
+
+ tcore_hal_link_user_data(hal, hal_data);
+ }
+
+ cdata->dlc_poll_src = 0;
+ //tcore_hal_set_power_state(h, TRUE);
+ func(h, cdata->cb_data);
+
+ return FALSE;
+
+error:
+ cdata->dlc_poll_src = 0;
+
+ return FALSE;
+}
+
+static int setup_mux(TcoreHal *h)
+{
+ struct gsm_config cfg;
+ int ldisc = N_GSM0710;
+ int ret = 0;
+ struct custom_data *data;
+ gint fd;
+
+ dbg("Function Enter");
+
+ data = tcore_hal_ref_user_data(h);
+ if (!data)
+ return TCORE_RETURN_FAILURE;
+
+ fd = g_io_channel_unix_get_fd(data->channel);
+ if (fd < 0){
+ err("fd not available\n");
+ return -1;
+ }
+
+ ret = ioctl(fd, TIOCSETD, &ldisc);
+ if (ret < 0) {
+ err("Set ioctl failed [%s]\n", strerror(errno));
+ return -1;
+ }
+
+ ret = ioctl(fd, TIOCGETD, &ldisc);
+ if (ret < 0) {
+ err("Get ioctl failed [%s]\n", strerror(errno));
+ return -1;
+ }
+
+ if (ldisc != N_GSM0710) {
+ err("Unable to set line discipline\n");
+ return -1;
+ }
+
+ /* configure mux */
+ memset(&cfg, 0, sizeof(struct gsm_config));
+
+ ret = ioctl(fd, GSMIOC_GETCONF, &cfg);
+ if (ret < 0) {
+ err("Get config ioctl failed [%s]\n", strerror(errno));
+ return -1;
+ }
+
+ cfg.encapsulation = 0; /* encoding -- set to basic */
+ cfg.initiator = 1; /* we are starting side */
+ cfg.mru = 32768; /* In specification 3GPP TS 27.010, 5.7.2 */
+ cfg.mtu = 32768; /* In specification 3GPP TS 27.010, 5.7.2 */
+
+ ret = ioctl(fd, GSMIOC_SETCONF, &cfg);
+ if (ret < 0) {
+ err("Set config ioctl failed [%s]\n", strerror(errno));
+ return -1;
+ }
+
+ data->dlc_poll_count = 0;
+ data->dlc_poll_src = g_timeout_add_full(G_PRIORITY_HIGH, 250,dlc_ready_check, h, 0);
+ return EXIT_SUCCESS;
+
+}
+
+static void on_response_setupmux(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ TcoreHal *hal = user_data;
+ struct custom_data *cdata = tcore_hal_ref_user_data(hal);
+ TReturn ret;
+
+ dbg("Entry");
+
+ if (cdata->watch_id > 0) {
+ g_source_remove(cdata->watch_id);
+ cdata->watch_id = 0;
+ }
+
+ /* Initialize CMUX */
+ ret = setup_mux(hal);
+ if (TCORE_RETURN_SUCCESS != ret)
+ err("Failed to initialize CMUX");
+
+ dbg("Exit");
+
+ return;
+}
+
+static TReturn hal_power(TcoreHal *hal, gboolean flag,
+ TcoreHalPowerCallBack func, void *user_data)
+{
+ struct custom_data *cdata = tcore_hal_ref_user_data(hal);
+
+ dbg("Entry");
+
+ /* power on */
+ if (flag == TRUE) {
+ TcorePending *pending;
+
+ cdata->cb = (void *)func;
+ cdata->cb_data = user_data;
+
+ tcore_hal_set_power_state(hal, TRUE);
+
+ pending = tcore_at_pending_new(NULL, "AT+CMUX=0,0,,1509,10,3,30,,", "+CMUX", TCORE_AT_NO_RESULT, on_response_setupmux, hal);
+
+ if (pending == NULL) {
+ err("Fail to create AT+CMUX pending request");
+
+ return TCORE_RETURN_FAILURE;
+ }
+
+ /* Send callback */
+ tcore_hal_send_request(hal, pending);
+ /* power off */
+ } else {
+ int i;
+
+ for (i = 0; i < g_mux_obj_ptr->channel_nb; i++) {
+
+ cdata = tcore_hal_ref_user_data(g_mux_obj_ptr->hal[i]);
+
+ if (cdata->watch_id > 0)
+ g_source_remove(cdata->watch_id);
+
+ g_io_channel_unref(cdata->channel);
+
+ tcore_hal_free(g_mux_obj_ptr->hal[i]);
+ free(cdata);
+ }
+
+ tcore_hal_set_power_state(hal, FALSE);
+
+ /* Free MUX Object */
+ free(g_mux_obj_ptr);
+ g_mux_obj_ptr = NULL;
+
+ dbg("Phone Power Off success.");
+ }
+
+ return TCORE_RETURN_SUCCESS;
+}
+
+
+static TReturn hal_send(TcoreHal *hal, unsigned int data_len, void *data)
+{
+ size_t bytes_written;
+ struct custom_data *cdata;
+
+ if (tcore_hal_get_power_state(hal) == FALSE)
+ return TCORE_RETURN_FAILURE;
+
+ cdata = tcore_hal_ref_user_data(hal);
+ if (!cdata)
+ return TCORE_RETURN_FAILURE;
+
+ bytes_written = channel_write(cdata->channel, data, data_len);
+ if(bytes_written != data_len) {
+ err("channel_write failed");
+
+ return TCORE_RETURN_FAILURE;
+ }
+
+ dbg("channel_write success (channel=%p, len=%d)", cdata->channel, bytes_written);
+
+ return TCORE_RETURN_SUCCESS;
+}
+
+static TReturn hal_link_object_channel(CoreObject *object)
+{
+ const char *name = tcore_object_ref_name(object);
+ TcoreHal *hal;
+ TReturn ret;
+
+ dbg("Entry");
+
+ hal = get_object_hal(name);
+ if (hal == NULL)
+ return TCORE_RETURN_EINVAL;
+
+ ret = tcore_object_set_hal(object, hal);
+
+ return ret;
+}
+
+static const char *setup_rawip(int fd)
+{
+ struct gsm_netconfig netconfig;
+ int index;
+ char *interface = NULL;
+ char ifname[IFNAMSIZ];
+
+ dbg("Entry");
+
+ memset(&netconfig, 0, sizeof(struct gsm_netconfig));
+
+ netconfig.adaption = 3;
+ netconfig.protocol = htons(ETH_P_IP);
+
+ if (fd < 0)
+ return NULL;
+
+ index = ioctl(fd, GSMIOC_ENABLE_NET, &netconfig);
+ if (index < 0) {
+ err("Set ioctl to create network failed [%s]\n", strerror(errno));
+ return NULL;
+ }
+
+ dbg("Net interface index: %d", index);
+
+ interface = if_indextoname(index, ifname);
+ if (interface == NULL) {
+ err("Interface index %d error %s", index, strerror(errno));
+ return NULL;
+ }
+
+ dbg("Interface name: %s", interface);
+
+ return interface;
+}
+
+static void on_response_setup_pdp(TcorePending *p, int data_len, const void *data, void *user_data)
+{
+ TcoreHal *hal = user_data;
+ const TcoreATResponse *resp = data;
+ const char *interface = NULL;
+ struct custom_data *cdata = tcore_hal_ref_user_data(hal);
+ CoreObject *co = tcore_pending_ref_core_object(p);
+ TcoreHalSetupPDPCallBack func = (TcoreHalSetupPDPCallBack)cdata->cb;
+
+ dbg("Entry");
+
+ if (resp->success) {
+ gint fd;
+
+ dbg("Response Ok");
+
+ fd = g_io_channel_unix_get_fd(cdata->channel);
+
+ interface = setup_rawip(fd);
+
+ cdata->rawip_enabled = TRUE;
+
+ func(co, interface, cdata->cb_data);
+
+ return;
+ }
+
+ func(co, NULL, cdata->cb_data);
+ dbg("Response NOk");
+
+ return;
+}
+
+static TReturn hal_setup_pdp(CoreObject *co, TcoreHalSetupPDPCallBack func,
+ void *user_data, unsigned int cid)
+{
+ TcorePending *pending = NULL;
+ TcoreHal *hal = NULL;
+ char *cmd_str = NULL;
+ struct custom_data *cdata;
+
+ dbg("Entry");
+
+ if (cid == 1)
+ hal = g_mux_obj_ptr->hal[CHANNEL_DATA_1];
+ else if (cid == 2)
+ hal = g_mux_obj_ptr->hal[CHANNEL_DATA_2];
+
+ if (hal == NULL) {
+ err("Fail to get channel data");
+ return TCORE_RETURN_FAILURE;
+ }
+
+ cdata = tcore_hal_ref_user_data(hal);
+
+ /*
+ * If data call was previously UP, we need to disable the
+ * network interface
+ */
+ if (cdata->rawip_enabled == TRUE) {
+ gint fd;
+ int ret;
+
+ fd = g_io_channel_unix_get_fd(cdata->channel);
+
+ ret = ioctl(fd, GSMIOC_DISABLE_NET, NULL);
+ if (ret < 0) {
+ err("Set ioctl to disable network interface failed [%s]\n", strerror(errno));
+ return TCORE_RETURN_FAILURE;
+ }
+
+ cdata->rawip_enabled = FALSE;
+ }
+
+ cdata->cb = (void *)func;
+ cdata->cb_data = user_data;
+
+ cmd_str = g_strdup_printf("AT+CGDATA=\"M-RAW_IP\",%d", cid);
+ pending = tcore_at_pending_new(co, cmd_str, NULL, TCORE_AT_NO_RESULT, on_response_setup_pdp, hal);
+ g_free(cmd_str);
+
+ if (pending == NULL) {
+ err("Fail to create AT+CGDATA pending request");
+
+ return TCORE_RETURN_FAILURE;
+ }
+
+ tcore_hal_send_request(hal, pending);
+
+ return TCORE_RETURN_SUCCESS;
+}
+
+static gboolean on_load()
+{
+ dbg("I'm load!");
+
+ return TRUE;
+}
+
+static gboolean on_init(TcorePlugin *plugin)
+{
+ TcoreHal *hal;
+ struct custom_data *data;
+
+ if (!plugin)
+ return FALSE;
+
+ dbg("I'm init!");
+
+ /* Phonet init */
+ data = g_new0(struct custom_data, 1);
+
+ data->channel = tty_open(DEVICE_IFX);
+ if (data->channel == NULL) {
+ err("Failed to open tty port - [%s]", strerror(errno));
+ g_free(data);
+ return FALSE;
+ }
+
+ dbg("tty port (%s) opened successfully", DEVICE_IFX);
+
+ /* HAL init */
+ hal = tcore_hal_new(plugin, "imc-pr3", &hops, TCORE_HAL_MODE_AT);
+ if (hal == NULL){
+ err("Failed to create HAL");
+ g_free(data);
+ return FALSE;
+ }
+
+ data->watch_id = register_gio_watch(data->channel, on_recv_tty_message, hal);
+
+ if (data->watch_id == 0){
+ err("Failed to register gio watch ");
+ free(data);
+ tcore_hal_free(hal);
+ g_io_channel_unref(data->channel);
+ return FALSE;
+ }
+
+ tcore_hal_link_user_data(hal, data);
+
+ dbg("HAL is imc-pr3: channel = %p, watch_id=%d ", data->channel, data->watch_id);
+
+ if (pr3_audio_init() != TRUE)
+ err("Error in audio initialization")
+
+ return TRUE;
+}
+
+static void on_unload(TcorePlugin *plugin)
+{
+ if (!plugin)
+ return;
+
+ if (pr3_audio_unload() != TRUE)
+ err("Error in audio unload")
+
+ dbg("I'm unload");
+}
+
+struct tcore_plugin_define_desc plugin_define_desc =
+{
+ .name = "IMC-PR3",
+ .priority = TCORE_PLUGIN_PRIORITY_HIGH,
+ .version = 1,
+ .load = on_load,
+ .init = on_init,
+ .unload = on_unload
+};