tist: Initial plugin implementation
authorSamuel Ortiz <sameo@linux.intel.com>
Thu, 10 Mar 2011 22:33:06 +0000 (23:33 +0100)
committerSamuel Ortiz <sameo@linux.intel.com>
Fri, 11 Mar 2011 01:10:47 +0000 (02:10 +0100)
This is a plugin to support TI's shared transport driver.

Makefile.plugins
configure.ac
plugins/tist.c [new file with mode: 0644]

index e83ba13..23524df 100644 (file)
@@ -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
index bae9ce8..3452e9c 100644 (file)
@@ -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 (file)
index 0000000..28498b6
--- /dev/null
@@ -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)