diff options
-rw-r--r-- | Makefile.plugins | 12 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | plugins/tist.c | 598 |
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) |