Path: blob/next/external/cache/sources/rtk_hciattach/hciattach.c
18115 views
/*1*2* BlueZ - Bluetooth protocol stack for Linux3*4* Copyright (C) 2000-2001 Qualcomm Incorporated5* Copyright (C) 2002-2003 Maxim Krasnyansky <[email protected]>6* Copyright (C) 2002-2010 Marcel Holtmann <[email protected]>7*8*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License as published by11* the Free Software Foundation; either version 2 of the License, or12* (at your option) any later version.13*14* This program is distributed in the hope that it will be useful,15* but WITHOUT ANY WARRANTY; without even the implied warranty of16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17* GNU General Public License for more details.18*19* You should have received a copy of the GNU General Public License20* along with this program; if not, write to the Free Software21* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA22*23*/2425#ifdef HAVE_CONFIG_H26#include <config.h>27#endif2829#define _GNU_SOURCE30#include <stdio.h>31#include <errno.h>32#include <fcntl.h>33#include <unistd.h>34#include <stdlib.h>35#include <string.h>36#include <signal.h>37#include <syslog.h>38#include <termios.h>39#include <time.h>40#include <sys/time.h>41#include <sys/poll.h>42#include <sys/param.h>43#include <sys/ioctl.h>44#include <sys/socket.h>45#include <sys/uio.h>46#include <sys/timerfd.h>4748#include "hciattach.h"4950#define RFKILL_NODE "/sys/class/rfkill/rfkill0/state"5152#ifdef NEED_PPOLL53#include "ppoll.h"54#endif5556/* #define SCHED_ENABLE */5758#ifdef SCHED_ENABLE59#include <sched.h>60#endif6162struct uart_t {63char *type;64int m_id;65int p_id;66int proto;67int init_speed;68int speed;69int flags;70int pm;71char *bdaddr;72int (*init) (int fd, struct uart_t *u, struct termios *ti);73int (*post) (int fd, struct uart_t *u, struct termios *ti);74};7576#define FLOW_CTL 0x000177#define ENABLE_PM 178#define DISABLE_PM 07980static volatile sig_atomic_t __io_canceled = 0;8182static void sig_hup(int sig)83{84RS_INFO("signal hup.");85}8687static void sig_term(int sig)88{89switch (sig) {90case SIGINT:91RS_INFO("signal int.");92break;93case SIGTERM:94RS_INFO("signal term.");95break;96}97__io_canceled = 1;98}99100static void sig_alarm(int sig)101{102RS_ERR("Initialization timed out.");103exit(1);104}105106static int uart_speed(int s)107{108switch (s) {109case 9600:110return B9600;111case 19200:112return B19200;113case 38400:114return B38400;115case 57600:116return B57600;117case 115200:118return B115200;119case 230400:120return B230400;121case 460800:122return B460800;123case 500000:124return B500000;125case 576000:126return B576000;127case 921600:128return B921600;129case 1000000:130return B1000000;131case 1152000:132return B1152000;133case 1500000:134return B1500000;135case 2000000:136return B2000000;137#ifdef B2500000138case 2500000:139return B2500000;140#endif141#ifdef B3000000142case 3000000:143return B3000000;144#endif145#ifdef B3500000146case 3500000:147return B3500000;148#endif149#ifdef B4000000150case 4000000:151return B4000000;152#endif153default:154return B57600;155}156}157158int set_speed(int fd, struct termios *ti, int speed)159{160if (cfsetospeed(ti, uart_speed(speed)) < 0)161return -errno;162163if (cfsetispeed(ti, uart_speed(speed)) < 0)164return -errno;165166if (tcsetattr(fd, TCSANOW, ti) < 0)167return -errno;168169return 0;170}171172static int realtek_init(int fd, struct uart_t *u, struct termios *ti)173{174175RS_INFO("Realtek Bluetooth init uart with init speed:%d, type:HCI UART %s",176u->init_speed,177(u->proto == HCI_UART_H4) ? "H4" : "H5");178return rtb_init(fd, u->proto, u->speed, ti);179}180181static int realtek_post(int fd, struct uart_t *u, struct termios *ti)182{183RS_INFO("Realtek Bluetooth post process");184return rtb_post(fd, u->proto, ti);185}186187struct uart_t uart[] = {188{ "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, DISABLE_PM, NULL, NULL},189190/* Realtek Bluetooth H4 */191{ "rtk_h4", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, 0, DISABLE_PM, NULL, realtek_init, realtek_post },192193/* Realtek Bluetooth H5 */194{ "rtk_h5", 0x0000, 0x0000, HCI_UART_3WIRE, 115200,115200, 0, DISABLE_PM, NULL, realtek_init, realtek_post },195196{ NULL, 0 }197};198199static struct uart_t * get_by_id(int m_id, int p_id)200{201int i;202for (i = 0; uart[i].type; i++) {203if (uart[i].m_id == m_id && uart[i].p_id == p_id)204return &uart[i];205}206return NULL;207}208209static struct uart_t * get_by_type(char *type)210{211int i;212for (i = 0; uart[i].type; i++) {213if (!strcmp(uart[i].type, type))214return &uart[i];215}216return NULL;217}218219/* Initialize UART driver */220static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)221{222struct termios ti;223int fd, i;224unsigned long flags = 0;225226if (raw)227flags |= 1 << HCI_UART_RAW_DEVICE;228229fd = open(dev, O_RDWR | O_NOCTTY);230if (fd < 0) {231RS_ERR("Can't open serial port, %d, %s", errno,232strerror(errno));233return -1;234}235236tcflush(fd, TCIOFLUSH);237238if (tcgetattr(fd, &ti) < 0) {239RS_ERR("Can't get port settings, %d, %s", errno,240strerror(errno));241return -1;242}243244cfmakeraw(&ti);245246ti.c_cflag |= CLOCAL;247if (u->flags & FLOW_CTL)248ti.c_cflag |= CRTSCTS;249else250ti.c_cflag &= ~CRTSCTS;251252if (tcsetattr(fd, TCSANOW, &ti) < 0) {253RS_ERR("Can't set port settings, %d, %s", errno,254strerror(errno));255return -1;256}257258/* Set initial baudrate */259if (set_speed(fd, &ti, u->init_speed) < 0) {260RS_ERR("Can't set initial baud rate, %d, %s", errno,261strerror(errno));262return -1;263}264265tcflush(fd, TCIOFLUSH);266267if (send_break) {268tcsendbreak(fd, 0);269usleep(500000);270}271272if (u->init && u->init(fd, u, &ti) < 0)273return -1;274275tcflush(fd, TCIOFLUSH);276277/* Set actual baudrate278* There is no need to change baudrate after uart init279* */280/* if (set_speed(fd, &ti, u->speed) < 0) {281* perror("Can't set baud rate");282* return -1;283* }284*/285286/* Set TTY to N_HCI line discipline */287i = N_HCI;288if (ioctl(fd, TIOCSETD, &i) < 0) {289RS_ERR("Can't set line discipline %d, %s", errno,290strerror(errno));291return -1;292}293294if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {295RS_ERR("Can't set UART flags %d, %s", errno, strerror(errno));296return -1;297}298299if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {300RS_ERR("Can't set device %d, %s", errno, strerror(errno));301return -1;302}303304if (u->post && u->post(fd, u, &ti) < 0)305return -1;306307return fd;308}309310static int reset_bluetooth(void)311{312313int fd;314char state[2];315int result;316317/* power off and power on BT */318fd = open(RFKILL_NODE, O_RDWR);319if (fd < 0) {320RS_ERR("Cannot open %s, %d %s", RFKILL_NODE, errno,321strerror(errno));322return -1;323}324state[0] = '0';325state[1] = '\0';326result = write(fd, state, strlen(state) + 1);327if (result != (strlen(state) + 1)) {328RS_ERR("Cannot write 0 to rfkill state %d %s", errno,329strerror(errno));330close(fd);331return -1;332}333334usleep(500000);335336state[0] = '1';337state[1] = '\0';338result = write(fd, state, strlen(state) + 1);339if (result != (strlen(state) + 1)) {340RS_ERR("Cannot write 1 to rfkill state %d %s", errno,341strerror(errno));342close(fd);343return -1;344}345346usleep(500000);347close(fd);348349return 0;350}351352static void usage(void)353{354RS_INFO("hciattach - HCI UART driver initialization utility");355RS_INFO("Usage:");356RS_INFO("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]");357RS_INFO("\thciattach -l");358}359360int main(int argc, char *argv[])361{362struct uart_t *u = NULL;363int detach, printpid, raw, opt, i, n, ld, err;364int to = 10;365int init_speed = 0;366int send_break = 0;367pid_t pid;368struct sigaction sa;369struct pollfd p;370sigset_t sigs;371char dev[PATH_MAX];372#ifdef SCHED_ENABLE373struct sched_param sched_par;374#endif375376detach = 1;377printpid = 0;378raw = 0;379380while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) {381switch(opt) {382case 'b':383send_break = 1;384break;385386case 'n':387detach = 0;388break;389390case 'p':391printpid = 1;392break;393394case 't':395to = atoi(optarg);396break;397398case 's':399init_speed = atoi(optarg);400break;401402case 'l':403for (i = 0; uart[i].type; i++) {404RS_INFO("%-10s0x%04x,0x%04x", uart[i].type,405uart[i].m_id, uart[i].p_id);406}407exit(0);408409case 'r':410raw = 1;411break;412413default:414usage();415exit(1);416}417}418419n = argc - optind;420if (n < 2) {421usage();422exit(1);423}424425for (n = 0; optind < argc; n++, optind++) {426char *opt;427428opt = argv[optind];429430switch(n) {431case 0:432dev[0] = 0;433if (!strchr(opt, '/'))434strcpy(dev, "/dev/");435strcat(dev, opt);436break;437438case 1:439if (strchr(argv[optind], ',')) {440int m_id, p_id;441sscanf(argv[optind], "%x,%x", &m_id, &p_id);442u = get_by_id(m_id, p_id);443} else {444u = get_by_type(opt);445}446447if (!u) {448RS_ERR("Unknown device type or id");449exit(1);450}451452break;453454case 2:455u->speed = atoi(argv[optind]);456break;457458case 3:459if (!strcmp("flow", argv[optind]))460u->flags |= FLOW_CTL;461else462u->flags &= ~FLOW_CTL;463break;464465case 4:466if (!strcmp("sleep", argv[optind]))467u->pm = ENABLE_PM;468else469u->pm = DISABLE_PM;470break;471472case 5:473u->bdaddr = argv[optind];474break;475}476}477478if (!u) {479RS_ERR("Unknown device type or id");480exit(1);481}482483start:484485#ifdef SCHED_ENABLE486RS_INFO("Increase the priority of the process with set sched");487memset(&sched_par, 0, sizeof(sched_par));488sched_par.sched_priority = 99;489err = sched_setscheduler(0, SCHED_FIFO, &sched_par);490if (err == -1) {491RS_ERR("Call sched_setscheduler error, %s",492strerror(errno));493}494/* #else495* RS_INFO("Increase the priority of the process with nice");496* err = nice(-20);497* if (err == -1) {498* RS_ERR("Call nice error, %s", strerror(errno));499* }500*/501#endif502503/* If user specified a initial speed, use that instead of504the hardware's default */505if (init_speed)506u->init_speed = init_speed;507508memset(&sa, 0, sizeof(sa));509sa.sa_flags = SA_NOCLDSTOP;510sa.sa_handler = sig_alarm;511sigaction(SIGALRM, &sa, NULL);512513/* 10 seconds should be enough for initialization */514alarm(to);515516n = init_uart(dev, u, send_break, raw);517if (n < 0) {518RS_ERR("Can't initialize device %d, %s", errno,519strerror(errno));520exit(1);521}522523RS_INFO("Device setup complete");524525alarm(0);526527memset(&sa, 0, sizeof(sa));528sa.sa_flags = SA_NOCLDSTOP;529sa.sa_handler = SIG_IGN;530sigaction(SIGCHLD, &sa, NULL);531sigaction(SIGPIPE, &sa, NULL);532533sa.sa_handler = sig_term;534sigaction(SIGTERM, &sa, NULL);535sigaction(SIGINT, &sa, NULL);536537sa.sa_handler = sig_hup;538sigaction(SIGHUP, &sa, NULL);539540if (detach) {541if ((pid = fork())) {542if (printpid)543RS_INFO("%d", pid);544return 0;545}546547for (i = 0; i < 20; i++)548if (i != n)549close(i);550}551552p.fd = n;553p.events = POLLERR | POLLHUP;554555sigfillset(&sigs);556sigdelset(&sigs, SIGCHLD);557sigdelset(&sigs, SIGPIPE);558sigdelset(&sigs, SIGTERM);559sigdelset(&sigs, SIGINT);560sigdelset(&sigs, SIGHUP);561562while (!__io_canceled) {563p.revents = 0;564err = ppoll(&p, 1, NULL, &sigs);565if (err < 0 && errno == EINTR) {566RS_INFO("Got EINTR.");567continue;568} if (err)569break;570}571572RS_INFO("err %d, p->revents %04x", err, p.revents);573574/* Restore TTY line discipline */575RS_INFO("Restore TTY line discipline");576ld = N_TTY;577if (ioctl(n, TIOCSETD, &ld) < 0) {578RS_ERR("Can't restore line discipline %d, %s", errno,579strerror(errno));580exit(1);581}582583if (p.revents & (POLLERR | POLLHUP)) {584RS_INFO("Recover...");585reset_bluetooth();586goto start;587}588589return 0;590}591592void util_hexdump(const uint8_t *buf, size_t len)593{594static const char hexdigits[] = "0123456789abcdef";595char str[16 * 3];596size_t i;597598if (!buf || !len)599return;600601for (i = 0; i < len; i++) {602str[((i % 16) * 3)] = hexdigits[buf[i] >> 4];603str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];604str[((i % 16) * 3) + 2] = ' ';605if ((i + 1) % 16 == 0) {606str[16 * 3 - 1] = '\0';607RS_INFO("%s", str);608}609}610611if (i % 16 > 0) {612str[(i % 16) * 3 - 1] = '\0';613RS_INFO("%s", str);614}615}616617int timeout_set(int fd, unsigned int msec)618{619struct itimerspec itimer;620unsigned int sec = msec / 1000;621622memset(&itimer, 0, sizeof(itimer));623itimer.it_interval.tv_sec = 0;624itimer.it_interval.tv_nsec = 0;625itimer.it_value.tv_sec = sec;626itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000 * 1000;627628return timerfd_settime(fd, 0, &itimer, NULL);629}630631632633