/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2010 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hciattach.h" #include "hciattach_aml.h" #include "hciattach_aml_usb.h" #include "aml_multibt.h" struct uart_t { char *type; int m_id; int p_id; int proto; int init_speed; int speed; int flags; int pm; char *dev; char *bdaddr; int (*init) (int fd, struct uart_t *u, struct termios *ti); int (*post) (int fd, struct uart_t *u, struct termios *ti); }; typedef struct { char *dev_name; char *type; } iface_info; #define FLOW_CTL 0x0001 #define AMP_DEV 0x0002 #define ENABLE_PM 1 #define DISABLE_PM 0 static struct uart_t *u = NULL; int g_usb_fd = -1; static bool usb_mod_flag = false; static bool stop_flag = false; static volatile sig_atomic_t __io_canceled = 0; static void sig_hup(int sig) { } static void sig_term(int sig) { fprintf(stderr, "sig_term\n"); __io_canceled = 1; } static void sig_alarm(int sig) { fprintf(stderr, "Initialization timed out.\n"); exit(1); } static void amlbt_sig_usr(int sig) { ALOGD("SIGUSR1"); stop_flag = true; __io_canceled = 1; } static int tty_get_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 B4000000 case 4000000: return B4000000; #endif default: return B57600; } } int set_speed(int fd, struct termios *ti, int speed) { if (cfsetospeed(ti, tty_get_speed(speed)) < 0) return -errno; if (cfsetispeed(ti, tty_get_speed(speed)) < 0) return -errno; if (tcsetattr(fd, TCSANOW, ti) < 0) return -errno; return 0; } /* * Read an HCI event from the given file descriptor. */ int read_hci_event(int fd, unsigned char* buf, int size) { int remain, r; int count = 0; if (size <= 0) return -1; while (1) { count = 0; /* The first byte identifies the packet type. For HCI event packets, it * should be 0x04, so we read until we get to the 0x04. */ while (1) { r = read(fd, buf, 1); if (r < 0) { if (errno == EINTR) // Retry if interrupted by a signal continue; return -1; } else if (r == 0) return -1; if (buf[0] == HCI_EVENT_PKT) break; } count++; /* The next two bytes are the event code and parameter total length. */ while (count < 3) { r = read(fd, buf + count, 3 - count); if (r < 0) { if (errno == EINTR) // Retry if interrupted by a signal continue; return -1; } else if (r == 0) return -1; count += r; } /* Now we read the parameters. */ if (buf[2] < (size - 3)) remain = buf[2]; else remain = size - 3; while ((count - 3) < remain) { r = read(fd, buf + count, remain - (count - 3)); if (r < 0) { if (errno == EINTR) // Retry if interrupted by a signal continue; return -1; } else if (r == 0) return -1; count += r; } if (buf[1] == AMLBT_FWLOG_EVENT) { ALOGD("ignore"); continue; } break; } return count; } static int aml(int fd, struct uart_t *u, struct termios *ti) { ALOGI("amlbt uart init"); return aml_init(fd, u->bdaddr); } struct uart_t uart[] = { /* AML ROME */ { "aml", 0x0000, 0x0000, HCI_UART_H4, 115200, 4000000, FLOW_CTL, DISABLE_PM, UART_DEV_PORT_BT, NULL, aml, NULL }, // aml_w2l_s support bt/zibgee/thread { "aml_15p4", 0x0000, 0x0000, HCI_UART_LL, 115200, 4000000, FLOW_CTL, DISABLE_PM, UART_DEV_PORT_BT, NULL, aml, NULL }, { NULL, 0 } }; const iface_info amlbt_iface[] = { { "aml_w1", "aml" }, { "aml_w1u", "aml" }, { "aml_w1u_s", "aml" }, { "aml_w2_p", "aml" }, { "aml_w2_s", "aml"}, { "aml_w2_u", "aml_usb"}, { "aml_w2l_s", "aml_15p4"}, { "aml_w2l_u", "aml_usb"}, { NULL, 0 } }; static int amlbt_get_init_cfg(struct uart_t **dev, amlbt_idx *idx) { dev_item *cur_dev = NULL; int i, j; cur_dev = get_amlbt_dev_item(); if (!cur_dev) { ALOGE("cur_dev is null"); return -1; } if (!cur_dev->dev_name) { ALOGE("dev_name is null"); return -1; } *idx = cur_dev->idx; for (i = 0; amlbt_iface[i].dev_name; i++) { if (strcmp(cur_dev->dev_name, amlbt_iface[i].dev_name) != 0) continue; if (strcmp(amlbt_iface[i].type, "aml_usb") == 0) { usb_mod_flag = true; return 0; } for (j = 0; uart[j].type; j++) { if (strcmp(amlbt_iface[i].type, uart[j].type) == 0) { *dev = &uart[j]; return 0; } } } ALOGE("fail"); return -1; } static struct uart_t * get_by_id(int m_id, int p_id) { int i; for (i = 0; uart[i].type; i++) { if (uart[i].m_id == m_id && uart[i].p_id == p_id) return &uart[i]; } return NULL; } static struct uart_t * get_by_type(char *type) { int i; for (i = 0; uart[i].type; i++) { if (!strcmp(uart[i].type, type)) return &uart[i]; } return NULL; } /* Initialize UART driver */ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw, amlbt_idx idx) { struct termios ti; int fd, i; unsigned long flags = 0; if (raw) flags |= 1 << HCI_UART_RAW_DEVICE; if (u->flags & AMP_DEV) flags |= 1 << HCI_UART_CREATE_AMP; 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"); goto fail; } cfmakeraw(&ti); ti.c_cflag |= CLOCAL; if (u->flags & FLOW_CTL) ti.c_cflag |= CRTSCTS; else ti.c_cflag &= ~CRTSCTS; if (tcsetattr(fd, TCSANOW, &ti) < 0) { perror("Can't set port settings"); goto fail; } /* Set initial baudrate */ if (set_speed(fd, &ti, u->init_speed) < 0) { perror("Can't set initial baud rate"); goto fail; } tcflush(fd, TCIOFLUSH); if (send_break) { tcsendbreak(fd, 0); usleep(500000); } if (u->init && u->init(fd, u, &ti) < 0) goto fail; tcflush(fd, TCIOFLUSH); /* Set actual baudrate */ if (set_speed(fd, &ti, u->speed) < 0) { perror("Can't set baud rate"); goto fail; } /* Set TTY to N_HCI line discipline */ i = (idx == W2L_UART) ? N_AML : N_HCI; ALOGI("tty disc: %d", i); if (ioctl(fd, TIOCSETD, &i) < 0) { perror("Can't set line discipline"); goto fail; } if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) { perror("Can't set UART flags"); goto fail; } if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) { perror("Can't set device"); goto fail; } if (u->post && u->post(fd, u, &ti) < 0) goto fail; return fd; fail: close(fd); return -1; } static void usage(void) { printf("hciattach - HCI UART driver initialization utility\n"); printf("Usage:\n"); printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed][-d debug]" " [speed] [flow|noflow]" " [sleep|nosleep] [bdaddr]\n"); printf("\thciattach -l\n"); } int main(int argc, char *argv[]) { int detach, printpid, raw, opt, i, n, ld; int to = 10; int init_speed = 0; int send_break = 0; int err = -1, val = -1; pid_t pid; struct sigaction sa; struct pollfd p; sigset_t sigs; char dev[PATH_MAX]; detach = 1; printpid = 0; raw = 0; amlbt_idx idx = UNK_AML_IDX; while ((opt=getopt(argc, argv, "d:bnpt:s:lr")) != EOF) { switch (opt) { case 'd': val = atoi(optarg); set_dbg_flag(val); break; case 'b': send_break = 1; break; case 'n': detach = 0; break; case 'p': printpid = 1; break; case 't': to = atoi(optarg); break; case 's': init_speed = atoi(optarg); break; case 'l': for (i = 0; uart[i].type; i++) { printf("%-10s0x%04x,0x%04x\n", uart[i].type, uart[i].m_id, uart[i].p_id); } exit(0); case 'r': raw = 1; break; default: 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/"); if (strlen(opt) > PATH_MAX - (strlen(dev) + 1)) { fprintf(stderr, "Invalid serial device\n"); exit(1); } strcat(dev, opt); break; case 1: if (strchr(argv[optind], ',')) { int m_id, p_id; sscanf(argv[optind], "%x,%x", &m_id, &p_id); u = get_by_id(m_id, p_id); } else { u = get_by_type(opt); } if (!u) { fprintf(stderr, "Unknown device type or id\n"); exit(1); } break; case 2: u->speed = atoi(argv[optind]); break; case 3: if (!strcmp("flow", argv[optind])) u->flags |= FLOW_CTL; else u->flags &= ~FLOW_CTL; break; case 4: if (!strcmp("sleep", argv[optind])) u->pm = ENABLE_PM; else u->pm = DISABLE_PM; break; case 5: u->bdaddr = argv[optind]; break; } } if (steps_table_poll() != 0) exit(1); if (amlbt_get_init_cfg(&u, &idx) != 0) exit(1); if (usb_mod_flag) { if (strcmp(dev, "/dev/aml_btusb")!= 0) { strcpy(dev, "/dev/aml_btusb"); //printf("usb mode dev is: %s\n", dev); } //get amlbt usb usb device file descriptor g_usb_fd = get_usb_fd(dev); if (g_usb_fd == -1) { fprintf(stderr, "open usb device failed!\n"); exit(1); } //init usb bt device err = aml_usb_init(g_usb_fd); if (err < 0) { fprintf(stderr, "aml usb device init failed!\n"); exit(1); } } else { if (!u) { ALOGE("Unknown amlbt uart dev type or id"); exit(1); } /* If user specified a initial speed, use that instead of the hardware's default */ if (init_speed) u->init_speed = init_speed; memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; sa.sa_handler = sig_alarm; sigaction(SIGALRM, &sa, NULL); /* 10 seconds should be enough for initialization */ alarm(to); n = init_uart(u->dev, u, send_break, raw, idx); if (n < 0) { perror("Can't initialize device"); exit(1); } ALOGD("amlbt uart dev setup complete"); alarm(0); 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); sa.sa_handler = amlbt_sig_usr; sigaction(SIGUSR1, &sa, NULL); if (detach) { if ((pid = fork())) { ALOGD("fork child process pid:%d", pid); if (printpid) printf("%d\n", pid); return 0; } #if 0 for (i = 0; i < 20; i++) if (i != n) close(i); #endif } p.fd = n; p.events = POLLERR | POLLHUP; sigfillset(&sigs); sigdelset(&sigs, SIGCHLD); sigdelset(&sigs, SIGPIPE); sigdelset(&sigs, SIGTERM); sigdelset(&sigs, SIGINT); sigdelset(&sigs, SIGHUP); sigdelset(&sigs, SIGUSR1); 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) { ALOGE("Can't restore line discipline"); perror("Can't restore line discipline"); close(n); exit(1); } if (!stop_flag) { ALOGD("cur idx:%u", idx); amlbt_set_woble_cfg(n, idx); } else { amlbt_open_dev_w1u(idx); amlbt_set_power(idx, 0); amlbt_close_dev(); //amlbt_drv_rmmod(); } close(n); } ALOGD("quit"); return 0; }