diff options
Diffstat (limited to 'hci/src/userial_mct.c')
-rw-r--r-- | hci/src/userial_mct.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/hci/src/userial_mct.c b/hci/src/userial_mct.c new file mode 100644 index 0000000..6ccb40f --- /dev/null +++ b/hci/src/userial_mct.c @@ -0,0 +1,468 @@ +/****************************************************************************** + * + * 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_mct.c + * + * Description: Contains open/read/write/close functions on multi-channels + * + ******************************************************************************/ + +#define LOG_TAG "bt_userial_mct" + +#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 +******************************************************************************/ + +#define USERIAL_DBG TRUE + +#ifndef USERIAL_DBG +#define USERIAL_DBG FALSE +#endif + +#if (USERIAL_DBG == TRUE) +#define USERIALDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);} +#else +#define USERIALDBG(param, ...) {} +#endif + +#define MAX_SERIAL_PORT (USERIAL_PORT_3 + 1) + +enum { + USERIAL_RX_EXIT, + USERIAL_RX_FLOW_OFF, + USERIAL_RX_FLOW_ON +}; + +/****************************************************************************** +** Externs +******************************************************************************/ + +extern bt_vendor_interface_t *bt_vnd_if; +uint16_t hci_mct_receive_evt_msg(void); +uint16_t hci_mct_receive_acl_msg(void); + + +/****************************************************************************** +** Local type definitions +******************************************************************************/ + +typedef struct +{ + int fd[CH_MAX]; + uint8_t port; + pthread_t read_thread; + 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; + +/****************************************************************************** +** 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) + { + ALOGE("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 userial_evt_read_thread +** +** Description The reading thread on EVT and ACL_IN channels +** +** Returns void * +** +*******************************************************************************/ +static void *userial_read_thread(void *arg) +{ + fd_set input; + int n; + char reason = 0; + + USERIALDBG("Entering userial_read_thread()"); + + rx_flow_on = TRUE; + userial_running = 1; + + while (userial_running) + { + /* Initialize the input fd set */ + FD_ZERO(&input); + if (rx_flow_on == TRUE) + { + FD_SET(userial_cb.fd[CH_EVT], &input); + FD_SET(userial_cb.fd[CH_ACL_IN], &input); + } + + int fd_max = create_signal_fds(&input); + fd_max = (fd_max>userial_cb.fd[CH_EVT]) ? fd_max : userial_cb.fd[CH_EVT]; + fd_max = (fd_max>userial_cb.fd[CH_ACL_IN]) ? fd_max : userial_cb.fd[CH_ACL_IN]; + + /* Do the select */ + n = 0; + n = select(fd_max+1, &input, NULL, NULL, NULL); + if(is_signaled(&input)) + { + reason = reset_signal(); + if (reason == USERIAL_RX_EXIT) + { + ALOGI("exiting userial_read_thread"); + userial_running = 0; + break; + } + 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(userial_cb.fd[CH_EVT], &input)) + { + hci_mct_receive_evt_msg(); + } + + if (FD_ISSET(userial_cb.fd[CH_ACL_IN], &input)) + { + hci_mct_receive_acl_msg(); + } + } + else if (n < 0) + ALOGW( "select() Failed"); + else if (n == 0) + ALOGW( "Got a select() TIMEOUT"); + } /* while */ + + userial_running = 0; + USERIALDBG("Leaving userial_evt_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) +{ + int idx; + + USERIALDBG("userial_init"); + memset(&userial_cb, 0, sizeof(tUSERIAL_CB)); + for (idx=0; idx < CH_MAX; idx++) + userial_cb.fd[idx] = -1; + utils_queue_init(&(userial_cb.rx_q)); + return TRUE; +} + + +/******************************************************************************* +** +** Function userial_open +** +** Description Open Bluetooth device with the port ID +** +** Returns TRUE/FALSE +** +*******************************************************************************/ +uint8_t userial_open(uint8_t port) +{ + struct sched_param param; + int policy, result; + pthread_attr_t thread_attr; + + USERIALDBG("userial_open(port:%d)", port); + + if (userial_running) + { + /* Userial is open; close it first */ + userial_close(); + utils_delay(50); + } + + if (port >= MAX_SERIAL_PORT) + { + ALOGE("Port > MAX_SERIAL_PORT"); + return FALSE; + } + + /* Calling vendor-specific part */ + if (bt_vnd_if) + { + result = bt_vnd_if->op(BT_VND_OP_USERIAL_OPEN, &userial_cb.fd); + + if ((result != 2) && (result != 4)) + { + ALOGE("userial_open: wrong numbers of open fd in vendor lib [%d]!", + result); + ALOGE("userial_open: HCI MCT expects 2 or 4 open file descriptors"); + bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL); + return FALSE; + } + } + else + { + ALOGE("userial_open: missing vendor lib interface !!!"); + ALOGE("userial_open: unable to open BT transport"); + return FALSE; + } + + ALOGI("CMD=%d, EVT=%d, ACL_Out=%d, ACL_In=%d", \ + userial_cb.fd[CH_CMD], userial_cb.fd[CH_EVT], \ + userial_cb.fd[CH_ACL_OUT], userial_cb.fd[CH_ACL_IN]); + + if ((userial_cb.fd[CH_CMD] == -1) || (userial_cb.fd[CH_EVT] == -1) || + (userial_cb.fd[CH_ACL_OUT] == -1) || (userial_cb.fd[CH_ACL_IN] == -1)) + { + ALOGE("userial_open: failed to open BT transport"); + bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL); + return FALSE; + } + + userial_cb.port = port; + + /* Start listening thread */ + pthread_attr_init(&thread_attr); + + if (pthread_create(&(userial_cb.read_thread), &thread_attr, \ + userial_read_thread, NULL) != 0 ) + { + ALOGE("pthread_create failed!"); + bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL); + 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) + { + ALOGW("userial_open: pthread_setschedparam failed (%s)", \ + strerror(result)); + } + } + + return TRUE; +} + +/******************************************************************************* +** +** Function userial_read +** +** Description Read data from the userial channel +** +** 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(uint16_t msg_id, uint8_t *p_buffer, uint16_t len) +{ + int ret = -1; + int ch_idx = (msg_id == MSG_HC_TO_STACK_HCI_EVT) ? CH_EVT : CH_ACL_IN; + + ret = read(userial_cb.fd[ch_idx], p_buffer, (size_t)len); + if (ret <= 0) + ALOGW( "userial_read: read() returned %d!", ret); + + return (uint16_t) ((ret >= 0) ? ret : 0); +} + +/******************************************************************************* +** +** 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(uint16_t msg_id, uint8_t *p_data, uint16_t len) +{ + int ret, total = 0; + int ch_idx = (msg_id == MSG_STACK_TO_HC_HCI_CMD) ? CH_CMD : CH_ACL_OUT; + + while(len != 0) + { + ret = write(userial_cb.fd[ch_idx], 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 idx, result; + + USERIALDBG("userial_close"); + + if (userial_running) + send_wakeup_signal(USERIAL_RX_EXIT); + + if ((result=pthread_join(userial_cb.read_thread, NULL)) < 0) + ALOGE( "pthread_join() FAILED result:%d", result); + + /* Calling vendor-specific part */ + if (bt_vnd_if) + bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL); + + for (idx=0; idx < CH_MAX; idx++) + userial_cb.fd[idx] = -1; +} + +/******************************************************************************* +** +** 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; + } +} + |