blob: c71d9c7eca90d5d51d47ac3e7557cf38654ef1b5 [file] [log] [blame]
/*
* 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;
}