diff --git a/Makefile b/Makefile index 9ebfc85..910ef1e 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ DIAG := diag CFLAGS := -Wall -g -O2 LDFLAGS := -ludev -SRCS := diag.c diag_cntl.c hdlc.c mbuf.c peripheral.c socket.c util.c watch.c +SRCS := diag.c diag_cntl.c hdlc.c mbuf.c peripheral.c socket.c uart.c util.c watch.c OBJS := $(SRCS:.c=.o) $(DIAG): $(OBJS) diff --git a/diag.c b/diag.c index 9f54b69..c015cf9 100644 --- a/diag.c +++ b/diag.c @@ -232,25 +232,32 @@ static void usage(void) fprintf(stderr, "User space application for diag interface\n" "\n" - "usage: diag [-hs]\n" + "usage: diag [-hsu]\n" "\n" "options:\n" " -h show this usage\n" " -s \n" + " -u \n" ); + exit(1); } int main(int argc, char **argv) { - char *host_address = ""; + char *host_address = NULL; int host_port = DEFAULT_SOCKET_PORT; + char *uartdev = NULL; + int baudrate = DEFAULT_BAUD_RATE; char *token; int ret; int c; + if (argc == 1) + usage(); + for (;;) { - c = getopt(argc, argv, "hs:"); + c = getopt(argc, argv, "hs:u:"); if (c < 0) break; switch (c) { @@ -260,6 +267,12 @@ int main(int argc, char **argv) if (token) host_port = atoi(token); break; + case 'u': + uartdev = strtok(strdup(optarg), "@"); + token = strtok(NULL, ""); + if (token) + baudrate = atoi(token); + break; default: case 'h': usage(); @@ -267,9 +280,17 @@ int main(int argc, char **argv) } } - ret = diag_sock_connect(host_address, host_port); - if (ret < 0) - err(1, "failed to connect to client"); + if (host_address) { + ret = diag_sock_connect(host_address, host_port); + if (ret < 0) + err(1, "failed to connect to client"); + } else if (uartdev) { + ret = diag_uart_open(uartdev, baudrate); + if (ret < 0) + errx(1, "failed to open uart\n"); + } else { + errx(1, "no configured connection mode\n"); + } peripheral_init(); diff --git a/diag.h b/diag.h index eccfb07..5b831c6 100644 --- a/diag.h +++ b/diag.h @@ -35,6 +35,7 @@ #include "list.h" #define DEFAULT_SOCKET_PORT 2500 +#define DEFAULT_BAUD_RATE 115200 #define BIT(x) (1 << (x)) @@ -89,6 +90,7 @@ int diag_cmd_recv(int fd, void *data); int diag_data_recv(int fd, void *data); int diag_sock_connect(const char *hostname, unsigned short port); +int diag_uart_open(const char *uartname, unsigned int baudrate); void diag_client_add(struct diag_client *client); int diag_client_handle_command(struct diag_client *client, uint8_t *data, size_t len); diff --git a/uart.c b/uart.c new file mode 100644 index 0000000..6f2ea28 --- /dev/null +++ b/uart.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diag.h" +#include "hdlc.h" +#include "watch.h" + +#define APPS_BUF_SIZE 4096 + +static unsigned int check_baudrate(unsigned int baudrate) +{ + switch (baudrate) + { + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 115200: + return B115200; + default: + warn("Illegal baud rate %u!", baudrate); + return 0; + } +} + +static int diag_uart_recv(int fd, void* data) +{ + struct diag_client *client = (struct diag_client *)data; + uint8_t buf[APPS_BUF_SIZE] = { 0 }; + size_t msglen; + size_t len; + ssize_t n; + void *msg; + void *ptr; + + n = read(client->fd, buf, sizeof(buf)); + if ((n < 0) && (errno != EAGAIN)) { + warn("Failed to read from fd=%d\n", client->fd); + return n; + } + + ptr = buf; + len = n; + + for (;;) { + msg = hdlc_decode_one(&ptr, &len, &msglen); + if (!msg) + break; + + diag_client_handle_command(client, msg, msglen); + } + + return 0; +} + +int diag_uart_open(const char *uartname, unsigned int baudrate) +{ + struct diag_client *client; + int ret; + int fd; + struct termios options, options_save; + + baudrate = check_baudrate(baudrate); + if (baudrate == 0) + return -EINVAL; + + fd = open(uartname, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd < 0) + return -errno; + + ret = tcflush(fd, TCIOFLUSH); + if (ret < 0) + return -errno; + + ret = fcntl(fd, F_SETFL, 0); + if (ret < 0) + return -errno; + + ret = ioctl(fd, TCGETS, &options_save); + if (ret < 0) + return -errno; + + options = options_save; + options.c_cc[VTIME] = 0; /* inter-character timer unused */ + options.c_cc[VMIN] = 4; /* blocking read until 4 chars received */ + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + options.c_cflag &= ~CSIZE; + options.c_cflag |= (CS8 | CLOCAL | CREAD); + options.c_iflag = 0; + options.c_oflag = 0; + options.c_lflag = 0; + options.c_cflag = (options.c_cflag & ~CBAUD) | (baudrate & CBAUD); + + ret = ioctl(fd, TCSETS, &options); // TODO: need to call ioctl(ret, TCSETS, &options_save) to revert to original state + if (ret < 0) + return -errno; + + printf("Connected to %s@%d\n", uartname, baudrate); + + client = calloc(1, sizeof(*client)); + if (!client) + err(1, "failed to allocate client context\n"); + + client->fd = fd; + client->name = "UART client"; + + watch_add_readfd(ret, diag_uart_recv, client); + watch_add_writeq(client->fd, &client->outq); + + diag_client_add(client); + + return fd; +}