| /* |
| * Copyright (C) 2015 Marvell International Ltd. |
| * |
| * This software file (the "File") is distributed by Marvell International |
| * Ltd. under the terms of the GNU General Public License Version 2, June 1991 |
| * (the "License"). You may use, redistribute and/or modify this File in |
| * accordance with the terms and conditions of the License, a copy of which |
| * is available on the worldwide web at |
| * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. |
| * |
| * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE |
| * ARE EXPRESSLY DISCLAIMED. The License provides additional details about |
| * this warranty disclaimer. |
| */ |
| |
| /* |
| * Adaptation of Bluez hciattach.c |
| * |
| * Copyright (C) 2000-2001 Qualcomm Incorporated |
| * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> |
| * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org> |
| */ |
| |
| #define _GNU_SOURCE |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <syslog.h> |
| #include <termios.h> |
| #include <time.h> |
| #include <sys/time.h> |
| #include <sys/poll.h> |
| #include <sys/param.h> |
| #include <sys/ioctl.h> |
| #include <termios.h> |
| #include <linux/tty.h> |
| #include "near.h" |
| |
| #define FLOW_CTL 0x0001 |
| #define ENABLE_PM 1 |
| #define DISABLE_PM 0 |
| |
| struct nci_driver_info { |
| const char *name; |
| enum nci_uart_driver driver_id; |
| }; |
| |
| static struct nci_driver_info __nci_drivers[] = { |
| { .name = "marvell", .driver_id = NCI_UART_DRIVER_MARVELL }, |
| }; |
| |
| static volatile sig_atomic_t __io_canceled = 0; |
| |
| static void sig_hup(int sig) |
| { |
| } |
| |
| static void sig_term(int sig) |
| { |
| __io_canceled = 1; |
| } |
| |
| static int uart_speed(int s) |
| { |
| switch (s) { |
| case 9600: |
| return B9600; |
| case 19200: |
| return B19200; |
| case 38400: |
| return B38400; |
| case 57600: |
| return B57600; |
| case 115200: |
| return B115200; |
| case 230400: |
| return B230400; |
| case 460800: |
| return B460800; |
| case 500000: |
| return B500000; |
| case 576000: |
| return B576000; |
| case 921600: |
| return B921600; |
| case 1000000: |
| return B1000000; |
| case 1152000: |
| return B1152000; |
| case 1500000: |
| return B1500000; |
| case 2000000: |
| return B2000000; |
| #ifdef B2500000 |
| case 2500000: |
| return B2500000; |
| #endif |
| #ifdef B3000000 |
| case 3000000: |
| return B3000000; |
| #endif |
| #ifdef B3500000 |
| case 3500000: |
| return B3500000; |
| #endif |
| #ifdef B3710000 |
| case 3710000 |
| return B3710000; |
| #endif |
| #ifdef B4000000 |
| case 4000000: |
| return B4000000; |
| #endif |
| default: |
| return B57600; |
| } |
| } |
| |
| static int set_speed(int fd, struct termios *ti, int speed) |
| { |
| if (cfsetospeed(ti, uart_speed(speed)) < 0) |
| return -errno; |
| |
| if (cfsetispeed(ti, uart_speed(speed)) < 0) |
| return -errno; |
| |
| if (tcsetattr(fd, TCSANOW, ti) < 0) |
| return -errno; |
| |
| return 0; |
| } |
| |
| /* Initialize UART driver */ |
| static int init_uart(char *dev, const char *driver, int speed, int flow_control) |
| { |
| struct termios ti; |
| int fd; |
| unsigned int i; |
| int driver_id = -1; |
| |
| /* Find the driver */ |
| for (i = 0; i < sizeof (__nci_drivers) / sizeof (__nci_drivers[0]); ++i) |
| if (!strcmp(driver, __nci_drivers[i].name)) { |
| driver_id = __nci_drivers[i].driver_id; |
| break; |
| } |
| |
| if (driver_id < 0) { |
| errno = ENOENT; |
| perror("unknown driver"); |
| return -1; |
| } |
| |
| |
| fd = open(dev, O_RDWR | O_NOCTTY); |
| if (fd < 0) { |
| perror("Can't open serial port"); |
| return -1; |
| } |
| |
| tcflush(fd, TCIOFLUSH); |
| |
| if (tcgetattr(fd, &ti) < 0) { |
| perror("Can't get port settings"); |
| return -1; |
| } |
| |
| cfmakeraw(&ti); |
| |
| ti.c_cflag |= CLOCAL; |
| if (flow_control) |
| ti.c_cflag |= CRTSCTS; |
| else |
| ti.c_cflag &= ~CRTSCTS; |
| |
| if (tcsetattr(fd, TCSANOW, &ti) < 0) { |
| perror("Can't set port settings"); |
| return -1; |
| } |
| |
| /* Set initial baudrate */ |
| if (set_speed(fd, &ti, speed) < 0) { |
| perror("Can't set baud rate"); |
| return -1; |
| } |
| |
| tcflush(fd, TCIOFLUSH); |
| |
| /* Set TTY to N_NCI line discipline */ |
| i = N_NCI; |
| if (ioctl(fd, TIOCSETD, &i) < 0) { |
| perror("Can't set line discipline"); |
| return -1; |
| } |
| |
| if (ioctl(fd, NCIUARTSETDRIVER, driver_id) < 0) { |
| perror("Can't set driver"); |
| return -1; |
| } |
| |
| return fd; |
| } |
| |
| static void usage(void) |
| { |
| printf("nciattach - NCI UART driver initialization utility\n"); |
| printf("Usage:\n"); |
| printf("\tnciattach [-n] [-p] <tty> <driver> " |
| "[speed] [flow|noflow]\n"); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int detach, printpid, opt, i, n, ld, err; |
| pid_t pid; |
| int speed = 115200; |
| int flow_control = 0; |
| struct sigaction sa; |
| struct pollfd p; |
| sigset_t sigs; |
| char dev[PATH_MAX]; |
| char driver[32]; |
| |
| detach = 1; |
| printpid = 0; |
| |
| while ((opt=getopt(argc, argv, "np")) != EOF) { |
| switch(opt) { |
| case 'n': |
| detach = 0; |
| break; |
| |
| case 'p': |
| printpid = 1; |
| break; |
| |
| default: |
| usage(); |
| exit(1); |
| } |
| } |
| |
| n = argc - optind; |
| if (n < 2) { |
| usage(); |
| exit(1); |
| } |
| |
| for (n = 0; optind < argc; n++, optind++) { |
| char *opt; |
| |
| opt = argv[optind]; |
| |
| switch(n) { |
| case 0: |
| dev[0] = 0; |
| if (!strchr(opt, '/')) |
| strcpy(dev, "/dev/"); |
| strcat(dev, opt); |
| break; |
| |
| case 1: |
| memset(driver, 0, sizeof (driver)); |
| strcpy(driver, argv[optind]); |
| break; |
| |
| case 2: |
| speed = atoi(argv[optind]); |
| break; |
| |
| case 3: |
| if (!strcmp("flow", argv[optind])) |
| flow_control = 1; |
| else |
| flow_control = 0; |
| break; |
| } |
| } |
| |
| memset(&sa, 0, sizeof(sa)); |
| |
| n = init_uart(dev, driver, speed, flow_control); |
| if (n < 0) { |
| perror("Can't initialize device"); |
| exit(1); |
| } |
| |
| memset(&sa, 0, sizeof(sa)); |
| sa.sa_flags = SA_NOCLDSTOP; |
| sa.sa_handler = SIG_IGN; |
| sigaction(SIGCHLD, &sa, NULL); |
| sigaction(SIGPIPE, &sa, NULL); |
| |
| sa.sa_handler = sig_term; |
| sigaction(SIGTERM, &sa, NULL); |
| sigaction(SIGINT, &sa, NULL); |
| |
| sa.sa_handler = sig_hup; |
| sigaction(SIGHUP, &sa, NULL); |
| |
| if (detach) { |
| if ((pid = fork())) { |
| if (printpid) |
| printf("%d\n", pid); |
| return 0; |
| } |
| |
| for (i = 0; i < 20; i++) |
| if (i != n) |
| close(i); |
| } |
| |
| p.fd = n; |
| p.events = POLLERR | POLLHUP; |
| |
| sigfillset(&sigs); |
| sigdelset(&sigs, SIGCHLD); |
| sigdelset(&sigs, SIGPIPE); |
| sigdelset(&sigs, SIGTERM); |
| sigdelset(&sigs, SIGINT); |
| sigdelset(&sigs, SIGHUP); |
| |
| while (!__io_canceled) { |
| p.revents = 0; |
| err = ppoll(&p, 1, NULL, &sigs); |
| if (err < 0 && errno == EINTR) |
| continue; |
| if (err) |
| break; |
| } |
| |
| /* Restore TTY line discipline */ |
| ld = N_TTY; |
| if (ioctl(n, TIOCSETD, &ld) < 0) { |
| perror("Can't restore line discipline"); |
| exit(1); |
| } |
| |
| return 0; |
| } |