summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.plugins12
-rw-r--r--configure.ac6
-rw-r--r--plugins/tist.c598
3 files changed, 616 insertions, 0 deletions
diff --git a/Makefile.plugins b/Makefile.plugins
index e83ba132..23524dfd 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -271,6 +271,18 @@ plugins_nmcompat_la_LDFLAGS = $(plugin_ldflags)
endif
endif
+if TIST
+if TIST_BUILTIN
+builtin_modules += tist
+builtin_sources += plugins/tist.c
+else
+plugin_LTLIBRARIES += plugins/tist.la
+plugin_objects += $(plugins_tist_la_OBJECTS)
+plugins_tist_la_CFLAGS = $(plugin_cflags)
+plugins_tist_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
EXTRA_DIST += plugins/polkit.policy
plugins/net.connman.policy: plugins/polkit.policy
diff --git a/configure.ac b/configure.ac
index bae9ce83..3452e9c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -280,6 +280,12 @@ AC_ARG_ENABLE(nmcompat,
AM_CONDITIONAL(NMCOMPAT, test "${enable_nmcompat}" != "no")
AM_CONDITIONAL(NMCOMPAT_BUILTIN, test "${enable_nmcompat}" = "builtin")
+AC_ARG_ENABLE(tist,
+ AC_HELP_STRING([--enable-tist], [enable TI Shared Transport support]),
+ [enable_tist=${enableval}], [enable_tist="no"])
+AM_CONDITIONAL(TIST, test "${enable_tist}" != "no")
+AM_CONDITIONAL(TIST_BUILTIN, test "${enable_tist}" = "builtin")
+
AC_ARG_WITH(stats-max-file-size, AC_HELP_STRING([--with-stats-max-file-size=SIZE],
[Maximal size of a statistics round robin file]),
[stats_max_file_size=${withval}])
diff --git a/plugins/tist.c b/plugins/tist.c
new file mode 100644
index 00000000..28498b61
--- /dev/null
+++ b/plugins/tist.c
@@ -0,0 +1,598 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2011 Intel Corporation. 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
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+
+#define TIST_SYSFS_INSTALL "/sys/devices/platform/kim/install"
+#define TIST_SYSFS_UART "/sys/devices/platform/kim/dev_name"
+#define TIST_SYSFS_BAUD "/sys/devices/platform/kim/baud_rate"
+
+/* Shared transport line discipline */
+#define N_TI_WL 22
+
+static GIOChannel *install_channel = NULL;
+static GIOChannel *uart_channel = NULL;
+static char uart_dev_name[32];
+static unsigned long baud_rate = 0;
+
+static guint install_watch = 0;
+static guint uart_watch = 0;
+
+static gint install_count = 0;
+
+#define NCCS2 19
+struct termios2 {
+ tcflag_t c_iflag; /* input mode flags */
+ tcflag_t c_oflag; /* output mode flags */
+ tcflag_t c_cflag; /* control mode flags */
+ tcflag_t c_lflag; /* local mode flags */
+ cc_t c_line; /* line discipline */
+ cc_t c_cc[NCCS2]; /* control characters */
+ speed_t c_ispeed; /* input speed */
+ speed_t c_ospeed; /* output speed */
+};
+
+#define BOTHER 0x00001000
+
+/* HCI definitions */
+#define HCI_HDR_OPCODE 0xff36
+#define HCI_COMMAND_PKT 0x01
+#define HCI_EVENT_PKT 0x04
+#define EVT_CMD_COMPLETE 0x0E
+
+/* HCI Command structure to set the target baud rate */
+struct speed_change_cmd {
+ uint8_t uart_prefix;
+ uint16_t opcode;
+ uint8_t plen;
+ uint32_t speed;
+} __attribute__ ((packed));
+
+/* HCI Event structure to set the cusrom baud rate*/
+struct cmd_complete {
+ uint8_t uart_prefix;
+ uint8_t evt;
+ uint8_t plen;
+ uint8_t ncmd;
+ uint16_t opcode;
+ uint8_t status;
+ uint8_t data[16];
+} __attribute__ ((packed));
+
+static int read_baud_rate(unsigned long *baud)
+{
+ int err;
+ FILE *f;
+
+ DBG("");
+
+ f = fopen(TIST_SYSFS_BAUD, "r");
+ if (f == NULL)
+ return -EIO;
+
+ err = fscanf(f, "%lu", baud);
+ fclose(f);
+
+ DBG("baud rate %lu", *baud);
+
+ return err;
+}
+
+static int read_uart_name(char uart_name[])
+{
+ int err;
+ FILE *f;
+
+ DBG("");
+
+ memset(uart_name, 0, sizeof(uart_name));
+
+ f = fopen(TIST_SYSFS_UART, "r");
+ if (f == NULL)
+ return -EIO;
+
+ err = fscanf(f, "%s", uart_name);
+ fclose(f);
+
+ DBG("UART name %s", uart_name);
+
+ return err;
+}
+
+static int read_hci_event(int fd, unsigned char *buf, int size)
+{
+ int prefix_len, param_len;
+
+ if (size <= 0)
+ return -EINVAL;
+
+ /* First 3 bytes are prefix, event and param length */
+ prefix_len = read(fd, buf, 3);
+ if (prefix_len < 0)
+ return prefix_len;
+
+ if (prefix_len < 3) {
+ connman_error("Truncated HCI prefix %d bytes 0x%x",
+ prefix_len, buf[0]);
+ return -EIO;
+ }
+
+ DBG("type 0x%x event 0x%x param len %d", buf[0], buf[1], buf[2]);
+
+ param_len = buf[2];
+ if (param_len > size - 3) {
+ connman_error("Buffer is too small %d", size);
+ return -EINVAL;
+ }
+
+ return read(fd, buf + 3, param_len);
+}
+
+static int read_command_complete(int fd, unsigned short opcode)
+{
+ struct cmd_complete resp;
+ int err;
+
+ DBG("");
+
+ err = read_hci_event(fd, (unsigned char *)&resp, sizeof(resp));
+ if (err < 0)
+ return err;
+
+ DBG("HCI event %d bytes", err);
+
+ if (resp.uart_prefix != HCI_EVENT_PKT) {
+ connman_error("Not an event packet");
+ return -EIO;
+ }
+
+ if (resp.evt != EVT_CMD_COMPLETE) {
+ connman_error("Not a cmd complete event");
+ return -EIO;
+ }
+
+ if (resp.plen < 4) {
+ connman_error("HCI header length %d", resp.plen);
+ return -EIO;
+ }
+
+ if (resp.opcode != (unsigned short) opcode) {
+ connman_error("opcode 0x%04x 0x%04x", resp.opcode, opcode);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* The default baud rate is 115200 */
+static int set_default_baud_rate(int fd)
+{
+ struct termios ti;
+ int err;
+
+ DBG("");
+
+ err = tcflush(fd, TCIOFLUSH);
+ if (err < 0)
+ goto err;
+
+ err = tcgetattr(fd, &ti);
+ if (err < 0)
+ goto err;
+
+ cfmakeraw(&ti);
+
+ ti.c_cflag |= 1;
+ ti.c_cflag |= CRTSCTS;
+
+ err = tcsetattr(fd, TCSANOW, &ti);
+ if (err < 0)
+ goto err;
+
+ cfsetospeed(&ti, B115200);
+ cfsetispeed(&ti, B115200);
+
+ err = tcsetattr(fd, TCSANOW, &ti);
+ if (err < 0)
+ goto err;
+
+ err = tcflush(fd, TCIOFLUSH);
+ if (err < 0)
+ goto err;
+
+ return 0;
+
+err:
+ connman_error("%s", strerror(errno));
+
+ return err;
+}
+
+static int set_custom_baud_rate(int fd, unsigned long cus_baud_rate, int flow_ctrl)
+{
+ struct termios ti;
+ struct termios2 ti2;
+ int err;
+
+ DBG("baud rate %lu flow_ctrl %d", cus_baud_rate, flow_ctrl);
+
+ err = tcflush(fd, TCIOFLUSH);
+ if (err < 0)
+ goto err;
+
+ err = tcgetattr(fd, &ti);
+ if (err < 0)
+ goto err;
+
+ if (flow_ctrl)
+ ti.c_cflag |= CRTSCTS;
+ else
+ ti.c_cflag &= ~CRTSCTS;
+
+ /*
+ * Set the parameters associated with the UART
+ * The change will occur immediately by using TCSANOW.
+ */
+ err = tcsetattr(fd, TCSANOW, &ti);
+ if (err < 0)
+ goto err;
+
+ err = tcflush(fd, TCIOFLUSH);
+ if (err < 0)
+ goto err;
+
+ /* Set the actual baud rate */
+ err = ioctl(fd, TCGETS2, &ti2);
+ if (err < 0)
+ goto err;
+
+ ti2.c_cflag &= ~CBAUD;
+ ti2.c_cflag |= BOTHER;
+ ti2.c_ospeed = cus_baud_rate;
+
+ err = ioctl(fd, TCSETS2, &ti2);
+ if (err < 0)
+ goto err;
+
+ return 0;
+
+err:
+ DBG("%s", strerror(errno));
+
+ return err;
+}
+
+static gboolean uart_event(GIOChannel *channel,
+ GIOCondition cond, gpointer data)
+{
+ int uart_fd, ldisc;
+
+ DBG("");
+
+ uart_fd = g_io_channel_unix_get_fd(channel);
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+ connman_error("UART event 0x%x", cond);
+ if (uart_watch > 0)
+ g_source_remove(uart_watch);
+
+ goto err;
+ }
+
+ if (read_command_complete(uart_fd, HCI_HDR_OPCODE) < 0)
+ goto err;
+
+ if (set_custom_baud_rate(uart_fd, baud_rate, 1) < 0)
+ goto err;
+
+ ldisc = N_TI_WL;
+ if (ioctl(uart_fd, TIOCSETD, &ldisc) < 0)
+ goto err;
+
+ g_atomic_int_set(&install_count, 0);
+
+ return FALSE;
+
+err:
+ g_atomic_int_set(&install_count, 0);
+ g_io_channel_shutdown(channel, TRUE, NULL);
+ g_io_channel_unref(channel);
+
+ return FALSE;
+}
+
+static int install_ldisc(GIOChannel *channel, gboolean install)
+{
+ int uart_fd, err;
+ struct speed_change_cmd cmd;
+ GIOFlags flags;
+
+ DBG("%d %p", install, uart_channel);
+
+ if (install == FALSE) {
+ g_atomic_int_set(&install_count, 0);
+
+ if (uart_channel == NULL) {
+ DBG("UART channel is NULL");
+ return 0;
+ }
+
+ g_io_channel_shutdown(uart_channel, TRUE, NULL);
+ g_io_channel_unref(uart_channel);
+
+ uart_channel = NULL;
+
+ return 0;
+ }
+
+ if (uart_channel != NULL) {
+ g_io_channel_shutdown(uart_channel, TRUE, NULL);
+ g_io_channel_unref(uart_channel);
+ uart_channel = NULL;
+ }
+
+ DBG("opening %s custom baud %lu", uart_dev_name, baud_rate);
+
+ uart_fd = open(uart_dev_name, O_RDWR);
+ if (uart_fd < 0)
+ return -EIO;
+
+ uart_channel = g_io_channel_unix_new(uart_fd);
+ g_io_channel_set_close_on_unref(uart_channel, TRUE);
+
+ g_io_channel_set_encoding(uart_channel, NULL, NULL);
+ g_io_channel_set_buffered(uart_channel, FALSE);
+
+ flags = g_io_channel_get_flags(uart_channel);
+ flags |= G_IO_FLAG_NONBLOCK;
+ g_io_channel_set_flags(uart_channel, flags, NULL);
+
+ err = set_default_baud_rate(uart_fd);
+ if (err < 0) {
+ g_io_channel_shutdown(uart_channel, TRUE, NULL);
+ g_io_channel_unref(uart_channel);
+ uart_channel = NULL;
+
+ return err;
+ }
+
+ if (baud_rate == 115200) {
+ int ldisc;
+
+ ldisc = N_TI_WL;
+ if (ioctl(uart_fd, TIOCSETD, &ldisc) < 0) {
+ g_io_channel_shutdown(uart_channel, TRUE, NULL);
+ g_io_channel_unref(uart_channel);
+ uart_channel = NULL;
+ }
+
+ g_atomic_int_set(&install_count, 0);
+
+ return 0;
+ }
+
+ cmd.uart_prefix = HCI_COMMAND_PKT;
+ cmd.opcode = HCI_HDR_OPCODE;
+ cmd.plen = sizeof(unsigned long);
+ cmd.speed = baud_rate;
+
+ uart_watch = g_io_add_watch(uart_channel,
+ G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ uart_event, NULL);
+
+ err = write(uart_fd, &cmd, sizeof(cmd));
+ if (err < 0) {
+ connman_error("Write failed %d", err);
+
+ g_io_channel_shutdown(uart_channel, TRUE, NULL);
+ g_io_channel_unref(uart_channel);
+ uart_channel = NULL;
+ }
+
+ return err;
+}
+
+
+static gboolean install_event(GIOChannel *channel,
+ GIOCondition cond, gpointer data)
+{
+ GIOStatus status = G_IO_STATUS_NORMAL;
+ unsigned int install_state;
+ gboolean install;
+ char buf[8];
+ gsize len;
+
+ DBG("");
+
+ if (cond & (G_IO_HUP | G_IO_NVAL)) {
+ connman_error("install event 0x%x", cond);
+ return FALSE;
+ }
+
+ if (g_atomic_int_get(&install_count) != 0) {
+ status = g_io_channel_seek_position(channel, 0, G_SEEK_SET, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
+ g_io_channel_shutdown(channel, TRUE, NULL);
+ g_io_channel_unref(channel);
+ return FALSE;
+ }
+
+ /* Read the install value */
+ status = g_io_channel_read_chars(channel, (gchar *) buf, 8, &len, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
+ g_io_channel_shutdown(channel, TRUE, NULL);
+ g_io_channel_unref(channel);
+ return FALSE;
+ }
+
+ install_state = atoi(buf);
+ DBG("install event while installing %d %c", install_state, buf[0]);
+
+ return TRUE;
+ } else {
+ g_atomic_int_set(&install_count, 1);
+ }
+
+ status = g_io_channel_seek_position(channel, 0, G_SEEK_SET, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
+ g_io_channel_shutdown(channel, TRUE, NULL);
+ g_io_channel_unref(channel);
+ return FALSE;
+ }
+
+ /* Read the install value */
+ status = g_io_channel_read_chars(channel, (gchar *) buf, 8, &len, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
+ g_io_channel_shutdown(channel, TRUE, NULL);
+ g_io_channel_unref(channel);
+ return FALSE;
+ }
+
+ install_state = atoi(buf);
+
+ DBG("install state %d", install_state);
+
+ install = !!install_state;
+
+ if (install_ldisc(channel, install) < 0) {
+ connman_error("ldisc installation failed");
+ g_atomic_int_set(&install_count, 0);
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+
+static int tist_init(void)
+{
+ GIOStatus status = G_IO_STATUS_NORMAL;
+ GIOFlags flags;
+ unsigned int install_state;
+ char buf[8];
+ int fd, err;
+ gsize len;
+
+ err = read_uart_name(uart_dev_name);
+ if (err < 0) {
+ connman_error("Could not read the UART name");
+ return err;
+ }
+
+ err = read_baud_rate(&baud_rate);
+ if (err < 0) {
+ connman_error("Could not read the baud rate");
+ return err;
+ }
+
+ fd = open(TIST_SYSFS_INSTALL, O_RDONLY);
+ if (fd < 0) {
+ connman_error("Failed to open TI ST sysfs install file");
+ return -EIO;
+ }
+
+ install_channel = g_io_channel_unix_new(fd);
+ g_io_channel_set_close_on_unref(install_channel, TRUE);
+
+ g_io_channel_set_encoding(install_channel, NULL, NULL);
+ g_io_channel_set_buffered(install_channel, FALSE);
+
+ flags = g_io_channel_get_flags(install_channel);
+ flags |= G_IO_FLAG_NONBLOCK;
+ g_io_channel_set_flags(install_channel, flags, NULL);
+
+ status = g_io_channel_read_chars(install_channel, (gchar *) buf, 8,
+ &len, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
+ g_io_channel_shutdown(install_channel, TRUE, NULL);
+ g_io_channel_unref(install_channel);
+ return status;
+ }
+
+ status = g_io_channel_seek_position(install_channel, 0, G_SEEK_SET, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
+ connman_error("Initial seek failed");
+ g_io_channel_shutdown(install_channel, TRUE, NULL);
+ g_io_channel_unref(install_channel);
+ return -EIO;
+ }
+
+ install_state = atoi(buf);
+
+ DBG("Initial state %d", install_state);
+
+ install_watch = g_io_add_watch_full(install_channel, G_PRIORITY_HIGH,
+ G_IO_PRI | G_IO_ERR,
+ install_event, NULL, NULL);
+
+ if (install_state) {
+ g_atomic_int_set(&install_count, 1);
+ err = install_ldisc(install_channel, TRUE);
+ if (err < 0) {
+ connman_error("ldisc installtion failed");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
+static void tist_exit(void)
+{
+
+ if (install_watch > 0)
+ g_source_remove(install_watch);
+
+ DBG("uart_channel %p", uart_channel);
+
+ g_io_channel_shutdown(install_channel, TRUE, NULL);
+ g_io_channel_unref(install_channel);
+
+ if (uart_channel != NULL) {
+ g_io_channel_shutdown(uart_channel, TRUE, NULL);
+ g_io_channel_unref(uart_channel);
+ uart_channel = NULL;
+ }
+
+}
+
+CONNMAN_PLUGIN_DEFINE(tist, "TI shared transport support", VERSION,
+ CONNMAN_PLUGIN_PRIORITY_DEFAULT, tist_init, tist_exit)