diff options
Diffstat (limited to 'hci/src/userial.c')
-rw-r--r-- | hci/src/userial.c | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/hci/src/userial.c b/hci/src/userial.c new file mode 100644 index 0000000..7385480 --- /dev/null +++ b/hci/src/userial.c @@ -0,0 +1,597 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * This program is the proprietary software of Broadcom Corporation and/or its + * licensors, and may only be used, duplicated, modified or distributed + * pursuant to the terms and conditions of a separate, written license + * agreement executed between you and Broadcom (an "Authorized License"). + * Except as set forth in an Authorized License, Broadcom grants no license + * (express or implied), right to use, or waiver of any kind with respect to + * the Software, and Broadcom expressly reserves all rights in and to the + * Software and all intellectual property rights therein. + * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS + * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE + * ALL USE OF THE SOFTWARE. + * + * Except as expressly set forth in the Authorized License, + * + * 1. This program, including its structure, sequence and organization, + * constitutes the valuable trade secrets of Broadcom, and you shall + * use all reasonable efforts to protect the confidentiality thereof, + * and to use this information only in connection with your use of + * Broadcom integrated circuit products. + * + * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED + * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES, + * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, + * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY + * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, + * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, + * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR + * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING OUT + * OF USE OR PERFORMANCE OF THE SOFTWARE. + * + * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM OR + * ITS LICENSORS BE LIABLE FOR + * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY + * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO + * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR + * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE + * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE + * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF + * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Filename: userial.c + * + * Description: Contains open/read/write/close functions on serial port + * + ******************************************************************************/ + +#define LOG_TAG "bt_userial" + +#include <utils/Log.h> +#include <pthread.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <sys/socket.h> +#include "bt_hci_bdroid.h" +#include "userial.h" +#include "utils.h" +#include "bt_vendor_lib.h" + +/****************************************************************************** +** Constants & Macros +******************************************************************************/ + +#ifndef USERIAL_DBG +#define USERIAL_DBG FALSE +#endif + +#if (USERIAL_DBG == TRUE) +#define USERIALDBG(param, ...) {LOGD(param, ## __VA_ARGS__);} +#else +#define USERIALDBG(param, ...) {} +#endif + +#define MAX_SERIAL_PORT (USERIAL_PORT_3 + 1) +#define READ_LIMIT (BTHC_USERIAL_READ_MEM_SIZE - BT_HC_HDR_SIZE) + +enum { + USERIAL_RX_EXIT, + USERIAL_RX_FLOW_OFF, + USERIAL_RX_FLOW_ON +}; + +/****************************************************************************** +** Externs +******************************************************************************/ + +extern bt_vendor_interface_t *bt_vnd_if; + +/****************************************************************************** +** Local type definitions +******************************************************************************/ + +typedef struct +{ + int sock; + uint8_t port; + pthread_t read_thread; + tUSERIAL_CFG cfg; + BUFFER_Q rx_q; + HC_BT_HDR *p_rx_hdr; +} tUSERIAL_CB; + +/****************************************************************************** +** Static variables +******************************************************************************/ + +static tUSERIAL_CB userial_cb; +static volatile uint8_t userial_running = 0; + +/* for friendly debugging outpout string */ +static uint32_t userial_baud_tbl[] = +{ + 300, /* USERIAL_BAUD_300 0 */ + 600, /* USERIAL_BAUD_600 1 */ + 1200, /* USERIAL_BAUD_1200 2 */ + 2400, /* USERIAL_BAUD_2400 3 */ + 9600, /* USERIAL_BAUD_9600 4 */ + 19200, /* USERIAL_BAUD_19200 5 */ + 57600, /* USERIAL_BAUD_57600 6 */ + 115200, /* USERIAL_BAUD_115200 7 */ + 230400, /* USERIAL_BAUD_230400 8 */ + 460800, /* USERIAL_BAUD_460800 9 */ + 921600, /* USERIAL_BAUD_921600 10 */ + 1000000, /* USERIAL_BAUD_1M 11 */ + 1500000, /* USERIAL_BAUD_1_5M 12 */ + 2000000, /* USERIAL_BAUD_2M 13 */ + 3000000, /* USERIAL_BAUD_3M 14 */ + 4000000 /* USERIAL_BAUD_4M 15 */ +}; + +/****************************************************************************** +** Static functions +******************************************************************************/ + +/***************************************************************************** +** Socket signal functions to wake up userial_read_thread for termination +** +** creating an unnamed pair of connected sockets +** - signal_fds[0]: join fd_set in select call of userial_read_thread +** - signal_fds[1]: trigger from userial_close +*****************************************************************************/ +static int signal_fds[2]={0,1}; +static uint8_t rx_flow_on = TRUE; +static inline int create_signal_fds(fd_set* set) +{ + if(signal_fds[0]==0 && socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fds)<0) + { + LOGE("create_signal_sockets:socketpair failed, errno: %d", errno); + return -1; + } + FD_SET(signal_fds[0], set); + return signal_fds[0]; +} +static inline int send_wakeup_signal(char sig_cmd) +{ + return send(signal_fds[1], &sig_cmd, sizeof(sig_cmd), 0); +} +static inline char reset_signal() +{ + char sig_recv = -1; + recv(signal_fds[0], &sig_recv, sizeof(sig_recv), MSG_WAITALL); + return sig_recv; +} +static inline int is_signaled(fd_set* set) +{ + return FD_ISSET(signal_fds[0], set); +} + +/******************************************************************************* +** +** Function select_read +** +** Description check if fd is ready for reading and listen for termination +** signal. need to use select in order to avoid collision +** between read and close on the same fd +** +** Returns -1: termination +** >=0: numbers of bytes read back from fd +** +*******************************************************************************/ +static int select_read(int fd, uint8_t *pbuf, int len) +{ + fd_set input; + int n = 0, ret = -1; + char reason = 0; + + while (userial_running) + { + /* Initialize the input fd set */ + FD_ZERO(&input); + if (rx_flow_on == TRUE) + { + FD_SET(fd, &input); + } + int fd_max = create_signal_fds(&input); + fd_max = fd_max > fd ? fd_max : fd; + + /* Do the select */ + n = select(fd_max+1, &input, NULL, NULL, NULL); + if(is_signaled(&input)) + { + reason = reset_signal(); + if (reason == USERIAL_RX_EXIT) + { + USERIALDBG("RX termination"); + return -1; + } + else if (reason == USERIAL_RX_FLOW_OFF) + { + USERIALDBG("RX flow OFF"); + rx_flow_on = FALSE; + } + else if (reason == USERIAL_RX_FLOW_ON) + { + USERIALDBG("RX flow ON"); + rx_flow_on = TRUE; + } + } + + if (n > 0) + { + /* We might have input */ + if (FD_ISSET(fd, &input)) + { + ret = read(fd, pbuf, (size_t)len); + if (0 == ret) + LOGW( "read() returned 0!" ); + + return ret; + } + } + else if (n < 0) + LOGW( "select() Failed"); + else if (n == 0) + LOGW( "Got a select() TIMEOUT"); + + } + + return ret; +} + +/******************************************************************************* +** +** Function userial_read_thread +** +** Description +** +** Returns void * +** +*******************************************************************************/ +static void *userial_read_thread(void *arg) +{ + int rx_length = 0; + HC_BT_HDR *p_buf = NULL; + uint8_t *p; + + USERIALDBG("Entering userial_read_thread()"); + + rx_flow_on = TRUE; + userial_running = 1; + + while (userial_running) + { + if (bt_hc_cbacks) + { + p_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc( \ + BTHC_USERIAL_READ_MEM_SIZE); + } + else + p_buf = NULL; + + if (p_buf != NULL) + { + p_buf->offset = 0; + p_buf->layer_specific = 0; + + p = (uint8_t *) (p_buf + 1); + rx_length = select_read(userial_cb.sock, p, READ_LIMIT); + } + else + { + rx_length = 0; + utils_delay(100); + LOGW("userial_read_thread() failed to gain buffers"); + continue; + } + + + if (rx_length > 0) + { + p_buf->len = (uint16_t)rx_length; + utils_enqueue(&(userial_cb.rx_q), p_buf); + bthc_signal_event(HC_EVENT_RX); + } + else /* either 0 or < 0 */ + { + LOGW("select_read return size <=0:%d, exiting userial_read_thread",\ + rx_length); + /* if we get here, we should have a buffer */ + bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1)); + /* negative value means exit thread */ + break; + } + } /* for */ + + userial_running = 0; + USERIALDBG("Leaving userial_read_thread()"); + pthread_exit(NULL); + + return NULL; // Compiler friendly +} + + +/***************************************************************************** +** Userial API Functions +*****************************************************************************/ + +/******************************************************************************* +** +** Function userial_init +** +** Description Initializes the userial driver +** +** Returns TRUE/FALSE +** +*******************************************************************************/ +uint8_t userial_init(void) +{ + USERIALDBG("userial_init"); + memset(&userial_cb, 0, sizeof(tUSERIAL_CB)); + userial_cb.sock = -1; + utils_queue_init(&(userial_cb.rx_q)); + return TRUE; +} + + +/******************************************************************************* +** +** Function userial_open +** +** Description Open the indicated serial port with the given configuration +** +** Returns TRUE/FALSE +** +*******************************************************************************/ +uint8_t userial_open(uint8_t port, tUSERIAL_CFG *p_cfg) +{ + struct sched_param param; + int policy, result; + pthread_attr_t thread_attr; + + USERIALDBG("userial_open(port:%d, baud:%d)", port, p_cfg->baud); + + if (userial_running) + { + /* Userial is open; close it first */ + userial_close(); + utils_delay(50); + } + + if (port >= MAX_SERIAL_PORT) + { + LOGE("Port > MAX_SERIAL_PORT"); + return FALSE; + } + + /* Calling vendor-specific part */ + if (bt_vnd_if) + { + userial_cb.sock = bt_vnd_if->op(BT_VND_OP_USERIAL_OPEN, p_cfg); + } + else + { + LOGE("userial_open: missing vendor lib interface !!!"); + LOGE("userial_open: unable to open UART port"); + return FALSE; + } + + if (userial_cb.sock == -1) + { + LOGE("userial_open: failed to open UART port"); + return FALSE; + } + + + USERIALDBG( "sock = %d", userial_cb.sock); + + userial_cb.port = port; + memcpy(&userial_cb.cfg, p_cfg, sizeof(tUSERIAL_CFG)); + + pthread_attr_init(&thread_attr); + + if (pthread_create(&(userial_cb.read_thread), &thread_attr, \ + userial_read_thread, NULL) != 0 ) + { + LOGE("pthread_create failed!"); + return FALSE; + } + + if(pthread_getschedparam(userial_cb.read_thread, &policy, ¶m)==0) + { + policy = BTHC_LINUX_BASE_POLICY; +#if (BTHC_LINUX_BASE_POLICY!=SCHED_NORMAL) + param.sched_priority = BTHC_USERIAL_READ_THREAD_PRIORITY; +#endif + result = pthread_setschedparam(userial_cb.read_thread, policy, ¶m); + if (result != 0) + { + LOGW("userial_open: pthread_setschedparam failed (%s)", \ + strerror(result)); + } + } + + return TRUE; +} + +/******************************************************************************* +** +** Function userial_read +** +** Description Read data from the userial port +** +** Returns Number of bytes actually read from the userial port and +** copied into p_data. This may be less than len. +** +*******************************************************************************/ +uint16_t userial_read(uint8_t *p_buffer, uint16_t len) +{ + uint16_t total_len = 0; + uint16_t copy_len = 0; + uint8_t *p_data = NULL; + + do + { + if(userial_cb.p_rx_hdr != NULL) + { + p_data = ((uint8_t *)(userial_cb.p_rx_hdr + 1)) + \ + (userial_cb.p_rx_hdr->offset); + + if((userial_cb.p_rx_hdr->len) <= (len - total_len)) + copy_len = userial_cb.p_rx_hdr->len; + else + copy_len = (len - total_len); + + memcpy((p_buffer + total_len), p_data, copy_len); + + total_len += copy_len; + + userial_cb.p_rx_hdr->offset += copy_len; + userial_cb.p_rx_hdr->len -= copy_len; + + if(userial_cb.p_rx_hdr->len == 0) + { + if (bt_hc_cbacks) + bt_hc_cbacks->dealloc((TRANSAC) userial_cb.p_rx_hdr, \ + (char *) (userial_cb.p_rx_hdr+1)); + + userial_cb.p_rx_hdr = NULL; + } + } + + if(userial_cb.p_rx_hdr == NULL) + { + userial_cb.p_rx_hdr=(HC_BT_HDR *)utils_dequeue(&(userial_cb.rx_q)); + } + } while ((userial_cb.p_rx_hdr != NULL) && (total_len < len)); + + return total_len; +} + +/******************************************************************************* +** +** Function userial_write +** +** Description Write data to the userial port +** +** Returns Number of bytes actually written to the userial port. This +** may be less than len. +** +*******************************************************************************/ +uint16_t userial_write(uint8_t *p_data, uint16_t len) +{ + int ret, total = 0; + + while(len != 0) + { + ret = write(userial_cb.sock, p_data+total, len); + total += ret; + len -= ret; + } + + return ((uint16_t)total); +} + +/******************************************************************************* +** +** Function userial_close +** +** Description Close the userial port +** +** Returns None +** +*******************************************************************************/ +void userial_close(void) +{ + int result; + TRANSAC p_buf; + + USERIALDBG("userial_close(sock:%d)", userial_cb.sock); + + if (userial_running) + send_wakeup_signal(USERIAL_RX_EXIT); + + if ((result=pthread_join(userial_cb.read_thread, NULL)) < 0) + LOGE( "pthread_join() FAILED result:%d", result); + + /* Calling vendor-specific part */ + if (bt_vnd_if) + bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL); + + if ((result=close(userial_cb.sock)) < 0) + LOGE( "close(sock:%d) FAILED result:%d", userial_cb.sock, result); + + userial_cb.sock = -1; + + if (bt_hc_cbacks) + { + while ((p_buf = utils_dequeue (&(userial_cb.rx_q))) != NULL) + { + bt_hc_cbacks->dealloc(p_buf, (char *) ((HC_BT_HDR *)p_buf+1)); + } + } +} + +/******************************************************************************* +** +** Function userial_change_baud +** +** Description Change baud rate of userial port +** +** Returns None +** +*******************************************************************************/ +void userial_change_baud(uint8_t baud) +{ + USERIALDBG("userial_change_baud: Closing UART Port"); + userial_close(); + + utils_delay(100); + + /* change baud rate in settings - leave everything else the same */ + userial_cb.cfg.baud = baud; + + LOGI("userial_change_rate: Attempting to reopen the UART Port at %i", \ + (unsigned int)userial_baud_tbl[baud]); + + userial_open(userial_cb.port, &userial_cb.cfg); +} + +/******************************************************************************* +** +** Function userial_ioctl +** +** Description ioctl inteface +** +** Returns None +** +*******************************************************************************/ +void userial_ioctl(userial_ioctl_op_t op, void *p_data) +{ + switch(op) + { + case USERIAL_OP_RXFLOW_ON: + if (userial_running) + send_wakeup_signal(USERIAL_RX_FLOW_ON); + break; + + case USERIAL_OP_RXFLOW_OFF: + if (userial_running) + send_wakeup_signal(USERIAL_RX_FLOW_OFF); + break; + + case USERIAL_OP_INIT: + default: + break; + } +} + |