diff options
Diffstat (limited to 'src/bt-vendor_vfs/ant_native_chardev.c')
-rw-r--r-- | src/bt-vendor_vfs/ant_native_chardev.c | 1078 |
1 files changed, 1078 insertions, 0 deletions
diff --git a/src/bt-vendor_vfs/ant_native_chardev.c b/src/bt-vendor_vfs/ant_native_chardev.c new file mode 100644 index 0000000..a90c8c4 --- /dev/null +++ b/src/bt-vendor_vfs/ant_native_chardev.c @@ -0,0 +1,1078 @@ +/* + * ANT Stack + * + * Copyright 2011 Dynastream Innovations + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/******************************************************************************\ +* +* FILE NAME: ant_native_chardev.c +* +* BRIEF: +* This file provides the VFS implementation of ant_native.h +* VFS could be Character Device, TTY, etc. +* +* +\******************************************************************************/ + +#include <errno.h> +#include <fcntl.h> /* for open() */ +#include <linux/ioctl.h> /* For hard reset */ +#include <pthread.h> +#include <dlfcn.h> /* needed for runtime dll loading. */ +#include <stdint.h> /* for uint64_t */ +#include <sys/eventfd.h> /* For eventfd() */ +#include <unistd.h> /* for read(), write(), and close() */ + +#include "ant_types.h" +#include "ant_native.h" +#include "ant_version.h" + +#include "antradio_power.h" +#include "ant_rx_chardev.h" +#include "ant_hci_defines.h" +#include "ant_log.h" +#include "bt_vendor_lib.h" /* used by qualcomms code to call into libbt-vendor.so */ +#include <cutils/properties.h> /* used by qualcomms additions for logging. */ +// The following functions are dummy implementations of the callbacks required by libbt-vendor. +static void vendor_fwcfg_cb(bt_vendor_op_result_t result) { +} +static void vendor_scocfg_cb(bt_vendor_op_result_t result) { +} +static void vendor_lpm_vnd_cb(bt_vendor_op_result_t result) { +} +static void* vendor_alloc(int size) { + return NULL; +} +static void vendor_dealloc(void *p_buf) { +} +static uint8_t vendor_xmit_cb(uint16_t opcode, void *p_buf, tINT_CMD_CBACK p_cback) { + return 0; +} +static void vendor_epilog_cb(bt_vendor_op_result_t result) { +} + +// This struct is used to regsiter the dummy callbacks with libbt-vendor +bt_vendor_interface_t *vendor_interface=NULL; +static const bt_vendor_callbacks_t vendor_callbacks = { + sizeof(bt_vendor_callbacks_t), + vendor_fwcfg_cb, + vendor_scocfg_cb, + vendor_lpm_vnd_cb, + vendor_alloc, + vendor_dealloc, + vendor_xmit_cb, + vendor_epilog_cb +}; + +#if ANT_HCI_SIZE_SIZE > 1 +#include "ant_utils.h" // Put HCI Size value across multiple bytes +#endif + +#define MESG_BROADCAST_DATA_ID ((ANT_U8)0x4E) +#define MESG_ACKNOWLEDGED_DATA_ID ((ANT_U8)0x4F) +#define MESG_BURST_DATA_ID ((ANT_U8)0x50) +#define MESG_EXT_BROADCAST_DATA_ID ((ANT_U8)0x5D) +#define MESG_EXT_ACKNOWLEDGED_DATA_ID ((ANT_U8)0x5E) +#define MESG_EXT_BURST_DATA_ID ((ANT_U8)0x5F) +#define MESG_ADV_BURST_DATA_ID ((ANT_U8)0x72) + +static ant_rx_thread_info_t stRxThreadInfo; +static pthread_mutex_t stEnabledStatusLock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t stFlowControlLock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t stFlowControlCond = PTHREAD_COND_INITIALIZER; +ANTNativeANTStateCb g_fnStateCallback; + +static const uint64_t EVENT_FD_PLUS_ONE = 1L; + +static void ant_channel_init(ant_channel_info_t *pstChnlInfo, const char *pcCharDevName); + +//////////////////////////////////////////////////////////////////// +// ant_init +// +// Initialises the native environment. +// +// Parameters: +// - +// +// Returns: +// ANT_STATUS_SUCCESS if intialize completed, else ANT_STATUS_FAILED +// +// Psuedocode: +/* +Set variables to defaults +Initialise each supported path to chip +Setup eventfd object. +RESULT = ANT_STATUS_SUCCESS if no problems else ANT_STATUS_FAILED +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus ant_init(void) +{ + ANTStatus status = ANT_STATUS_FAILED; + ANT_FUNC_START(); + + stRxThreadInfo.stRxThread = 0; + stRxThreadInfo.ucRunThread = 0; + stRxThreadInfo.ucChipResetting = 0; + stRxThreadInfo.pstEnabledStatusLock = &stEnabledStatusLock; + g_fnStateCallback = 0; + +#ifdef ANT_DEVICE_NAME // Single transport path + ant_channel_init(&stRxThreadInfo.astChannels[SINGLE_CHANNEL], ANT_DEVICE_NAME); +#else // Separate data/command paths + ant_channel_init(&stRxThreadInfo.astChannels[COMMAND_CHANNEL], ANT_COMMANDS_DEVICE_NAME); + ant_channel_init(&stRxThreadInfo.astChannels[DATA_CHANNEL], ANT_DATA_DEVICE_NAME); +#endif // Separate data/command paths + + // Make the eventfd. Want it non blocking so that we can easily reset it by reading. + stRxThreadInfo.iRxShutdownEventFd = eventfd(0, EFD_NONBLOCK); + + // Check for error case + if(stRxThreadInfo.iRxShutdownEventFd == -1) + { + ANT_ERROR("ANT init failed. Could not create event fd. Reason: %s", strerror(errno)); + } else { + status = ANT_STATUS_SUCCESS; + } + + ANT_FUNC_END(); + return status; +} + +//////////////////////////////////////////////////////////////////// +// ant_deinit +// +// clean up eventfd object +// +// Parameters: +// - +// +// Returns: +// ANT_STATUS_SUCCESS +// +// Psuedocode: +/* +RESULT = SUCCESS +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus ant_deinit(void) +{ + ANTStatus result_status = ANT_STATUS_FAILED; + ANT_FUNC_START(); + + if(close(stRxThreadInfo.iRxShutdownEventFd) < 0) + { + ANT_ERROR("Could not close eventfd in deinit. Reason: %s", strerror(errno)); + } else { + result_status = ANT_STATUS_SUCCESS; + } + + ANT_FUNC_END(); + return result_status; +} + + +//////////////////////////////////////////////////////////////////// +// ant_enable_radio +// +// Powers on the ANT part and initialises the transport to the chip. +// Changes occur in part implementing ant_enable() call +// +// Parameters: +// - +// +// Returns: +// Success: +// ANT_STATUS_SUCCESS +// Failures: +// ANT_STATUS_TRANSPORT_INIT_ERR if could not enable +// ANT_STATUS_FAILED if failed to get mutex or init rx thread +// +// Psuedocode: +/* +LOCK enable_LOCK + State callback: STATE = ENABLING + ant enable + IF ant_enable success + State callback: STATE = ENABLED + RESULT = SUCCESS + ELSE + ant disable + State callback: STATE = Current state + RESULT = FAILURE + ENDIF +UNLOCK +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus ant_enable_radio(void) +{ + int iLockResult; + ANTStatus result_status = ANT_STATUS_FAILED; + ANT_FUNC_START(); + + ANT_DEBUG_V("getting stEnabledStatusLock in %s", __FUNCTION__); + iLockResult = pthread_mutex_lock(&stEnabledStatusLock); + if(iLockResult) { + ANT_ERROR("enable failed to get state lock: %s", strerror(iLockResult)); + goto out; + } + ANT_DEBUG_V("got stEnabledStatusLock in %s", __FUNCTION__); + + if (g_fnStateCallback) { + g_fnStateCallback(RADIO_STATUS_ENABLING); + } + + if (ant_enable() < 0) { + ANT_ERROR("ant enable failed: %s", strerror(errno)); + + ant_disable(); + + if (g_fnStateCallback) { + g_fnStateCallback(ant_radio_enabled_status()); + } + } else { + if (g_fnStateCallback) { + g_fnStateCallback(RADIO_STATUS_ENABLED); + } + + result_status = ANT_STATUS_SUCCESS; + } + + ANT_DEBUG_V("releasing stEnabledStatusLock in %s", __FUNCTION__); + pthread_mutex_unlock(&stEnabledStatusLock); + ANT_DEBUG_V("released stEnabledStatusLock in %s", __FUNCTION__); + +out: + ANT_FUNC_END(); + return result_status; +} + +//////////////////////////////////////////////////////////////////// +// ant_radio_hard_reset +// +// IF SUPPORTED triggers a hard reset of the chip providing ANT functionality. +// +// Parameters: +// - +// +// Returns: +// Success: +// ANT_STATUS_SUCCESS +// Failures: +// ANT_STATUS_NOT_SUPPORTED if the chip can't hard reset +// ANT_STATUS_FAILED if failed to get mutex or enable +// +// Psuedocode: +/* +IF Hard Reset not supported + RESULT = NOT SUPPORTED +ELSE + LOCK enable_LOCK + IF Lock failed + RESULT = FAILED + ELSE + Set Flag Rx thread that chip is resetting + FOR each path to chip + Send Reset IOCTL to path + ENDFOR + ant disable + ant enable + IF ant_enable success + State callback: STATE = RESET + RESULT = SUCCESS + ELSE + State callback: STATE = DISABLED + RESULT = FAILURE + ENDIF + Clear Flag Rx thread that chip is resetting + UNLOCK +ENDIF +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus ant_radio_hard_reset(void) +{ + ANTStatus result_status = ANT_STATUS_NOT_SUPPORTED; + ANT_FUNC_START(); + +#ifdef ANT_IOCTL_RESET + ant_channel_type eChannel; + int iLockResult; + + result_status = ANT_STATUS_FAILED; + + ANT_DEBUG_V("getting stEnabledStatusLock in %s", __FUNCTION__); + iLockResult = pthread_mutex_lock(&stEnabledStatusLock); + if(iLockResult) { + ANT_ERROR("enable failed to get state lock: %s", strerror(iLockResult)); + goto out; + } + ANT_DEBUG_V("got stEnabledStatusLock in %s", __FUNCTION__); + + stRxThreadInfo.ucChipResetting = 1; + if (g_fnStateCallback) + g_fnStateCallback(RADIO_STATUS_RESETTING); + +#ifdef ANT_IOCTL_RESET_PARAMETER + ioctl(stRxThreadInfo.astChannels[0].iFd, ANT_IOCTL_RESET, ANT_IOCTL_RESET_PARAMETER); +#else + ioctl(stRxThreadInfo.astChannels[0].iFd, ANT_IOCTL_RESET); +#endif // ANT_IOCTL_RESET_PARAMETER + + ant_disable(); + + if (ant_enable()) { /* failed */ + if (g_fnStateCallback) + g_fnStateCallback(RADIO_STATUS_DISABLED); + } else { /* success */ + if (g_fnStateCallback) + g_fnStateCallback(RADIO_STATUS_RESET); + result_status = ANT_STATUS_SUCCESS; + } + stRxThreadInfo.ucChipResetting = 0; + + ANT_DEBUG_V("releasing stEnabledStatusLock in %s", __FUNCTION__); + pthread_mutex_unlock(&stEnabledStatusLock); + ANT_DEBUG_V("released stEnabledStatusLock in %s", __FUNCTION__); +out: +#endif // ANT_IOCTL_RESET + + ANT_FUNC_END(); + return result_status; +} + +//////////////////////////////////////////////////////////////////// +// ant_disable_radio +// +// Powers off the ANT part and closes the transport to the chip. +// +// Parameters: +// - +// +// Returns: +// Success: +// ANT_STATUS_SUCCESS +// Failures: +// ANT_STATUS_FAILED if failed to get mutex +// +// Psuedocode: +/* +LOCK enable_LOCK + State callback: STATE = DISABLING + ant disable + State callback: STATE = Current state + RESULT = SUCCESS +UNLOCK +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus ant_disable_radio(void) +{ + int iLockResult; + ANTStatus ret = ANT_STATUS_FAILED; + ANT_FUNC_START(); + + ANT_DEBUG_V("getting stEnabledStatusLock in %s", __FUNCTION__); + iLockResult = pthread_mutex_lock(&stEnabledStatusLock); + if(iLockResult) { + ANT_ERROR("disable failed to get state lock: %s", strerror(iLockResult)); + goto out; + } + ANT_DEBUG_V("got stEnabledStatusLock in %s", __FUNCTION__); + + if (g_fnStateCallback) { + g_fnStateCallback(RADIO_STATUS_DISABLING); + } + + ant_disable(); + + if (g_fnStateCallback) { + g_fnStateCallback(ant_radio_enabled_status()); + } + + ret = ANT_STATUS_SUCCESS; + + ANT_DEBUG_V("releasing stEnabledStatusLock in %s", __FUNCTION__); + pthread_mutex_unlock(&stEnabledStatusLock); + ANT_DEBUG_V("released stEnabledStatusLock in %s", __FUNCTION__); + +out: + ANT_FUNC_END(); + return ret; +} + +//////////////////////////////////////////////////////////////////// +// ant_radio_enabled_status +// +// Gets the current chip/transport state; either disabled, disabling, +// enabling, enabled, or resetting. Determines this on the fly by checking +// if Rx thread is running and how many of the paths for the ANT chip have +// open VFS files. +// +// Parameters: +// - +// +// Returns: +// The current radio status (ANTRadioEnabledStatus) +// +// Psuedocode: +/* +IF Thread Resetting Flag is set + RESULT = Resetting +ELSE + COUNT the number of open files + IF Thread Run Flag is Not Set + IF there are open files OR Rx thread exists + RESULT = Disabling + ELSE + RESULT = Disabled + ENDIF + ELSE + IF All files are open (all paths) AND Rx thread exists + RESULT = ENABLED + ELSE IF there are open files (Not 0 open files) AND Rx thread exists + RESULT = UNKNOWN + ELSE (0 open files or Rx thread does not exist [while Thread Run set]) + RESULT = ENABLING + ENDIF + ENDIF +ENDIF +*/ +//////////////////////////////////////////////////////////////////// +ANTRadioEnabledStatus ant_radio_enabled_status(void) +{ + ant_channel_type eChannel; + int iOpenFiles = 0; + int iOpenThread; + ANTRadioEnabledStatus uiRet = RADIO_STATUS_UNKNOWN; + ANT_FUNC_START(); + + if (stRxThreadInfo.ucChipResetting) { + uiRet = RADIO_STATUS_RESETTING; + goto out; + } + + for (eChannel = 0; eChannel < NUM_ANT_CHANNELS; eChannel++) { + if (stRxThreadInfo.astChannels[eChannel].iFd != -1) { + iOpenFiles++; + } + } + + iOpenThread = (stRxThreadInfo.stRxThread) ? 1 : 0; + + if (!stRxThreadInfo.ucRunThread) { + if (iOpenFiles || iOpenThread) { + uiRet = RADIO_STATUS_DISABLING; + } else { + uiRet = RADIO_STATUS_DISABLED; + } + } else { + if ((iOpenFiles == NUM_ANT_CHANNELS) && iOpenThread) { + uiRet = RADIO_STATUS_ENABLED; + } else if (!iOpenFiles && iOpenThread) { + uiRet = RADIO_STATUS_UNKNOWN; + } else { + uiRet = RADIO_STATUS_ENABLING; + } + } + +out: + ANT_DEBUG_D("get radio enabled status returned %d", uiRet); + + ANT_FUNC_END(); + return uiRet; +} + +//////////////////////////////////////////////////////////////////// +// set_ant_rx_callback +// +// Sets which function to call when an ANT message is received. +// +// Parameters: +// rx_callback_func the ANTNativeANTEventCb function to be used for +// received messages (from all transport paths). +// +// Returns: +// ANT_STATUS_SUCCESS +// +// Psuedocode: +/* +FOR each transport path + Path Rx Callback = rx_callback_func +ENDFOR +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus set_ant_rx_callback(ANTNativeANTEventCb rx_callback_func) +{ + ANTStatus status = ANT_STATUS_SUCCESS; + ANT_FUNC_START(); + +#ifdef ANT_DEVICE_NAME // Single transport path + stRxThreadInfo.astChannels[SINGLE_CHANNEL].fnRxCallback = rx_callback_func; +#else // Separate data/command paths + stRxThreadInfo.astChannels[COMMAND_CHANNEL].fnRxCallback = rx_callback_func; + stRxThreadInfo.astChannels[DATA_CHANNEL].fnRxCallback = rx_callback_func; +#endif // Separate data/command paths + + ANT_FUNC_END(); + return status; +} + +//////////////////////////////////////////////////////////////////// +// set_ant_state_callback +// +// Sets which function to call when an ANT state change occurs. +// +// Parameters: +// state_callback_func the ANTNativeANTStateCb function to be used +// for received state changes. +// +// Returns: +// ANT_STATUS_SUCCESS +// +// Psuedocode: +/* + State Callback = state_callback_func +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus set_ant_state_callback(ANTNativeANTStateCb state_callback_func) +{ + ANTStatus status = ANT_STATUS_SUCCESS; + ANT_FUNC_START(); + + g_fnStateCallback = state_callback_func; + + ANT_FUNC_END(); + return status; +} + +//////////////////////////////////////////////////////////////////// +// ant_tx_message_flowcontrol_wait +// +// Sends an ANT message to the chip and waits for a CTS signal +// +// Parameters: +// eTxPath device to transmit message on +// eFlowMessagePath device that receives CTS +// ucMessageLength the length of the message +// pucMesg pointer to the message data +// +// Returns: +// Success: +// ANT_STATUS_SUCCESS +// Failure: +// ANT_STATUS_NOT_ENABLED +// +// Psuedocode: +/* + LOCK flow control + IF Lock failed + RESULT = FAILED + ELSE + SET flowMessagePath Flow Control response as FLOW_STOP + WRITE txBuffer to txPath (only length of packet part) + IF Wrote less then 0 bytes + Log error + RESULT = FAILED + ELSE IF Didn't write 'length of packet' bytes + Log error + RESULT = FAILED + ELSE + IF flowMessagePath Flow Control response is not FLOW_GO + WAIT until flowMessagePath Flow Control response is FLOW_GO, UNTIL FLOW_GO Wait Timeout seconds (10) from Now + IF error Waiting + IF error is Timeout + RESULT = HARDWARE ERROR + ELSE + RESULT = FAILED + ENDIF + ELSE + RESULT = SUCCESS + ENDIF + ELSE + RESULT = SUCCESS; + ENDIF + ENDIF + UNLOCK flow control + ENDIF +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus ant_tx_message_flowcontrol_wait(ant_channel_type eTxPath, ant_channel_type eFlowMessagePath, ANT_U8 ucMessageLength, ANT_U8 *pucTxMessage) +{ + int iMutexResult; + int iResult; + struct timespec stTimeout; + int iCondWaitResult; + ANTStatus status = ANT_STATUS_FAILED; + ANT_FUNC_START(); + + ANT_DEBUG_V("getting stFlowControlLock in %s", __FUNCTION__); + iMutexResult = pthread_mutex_lock(&stFlowControlLock); + if (iMutexResult) { + ANT_ERROR("failed to lock flow control mutex during tx: %s", strerror(iMutexResult)); + goto out; + } + ANT_DEBUG_V("got stFlowControlLock in %s", __FUNCTION__); + + stRxThreadInfo.astChannels[eFlowMessagePath].ucFlowControlResp = ANT_FLOW_STOP; + +#ifdef ANT_FLOW_RESEND + // Store Tx message so can resend it from Rx thread + stRxThreadInfo.astChannels[eFlowMessagePath].ucResendMessageLength = ucMessageLength; + stRxThreadInfo.astChannels[eFlowMessagePath].pucResendMessage = pucTxMessage; +#endif // ANT_FLOW_RESEND + + iResult = write(stRxThreadInfo.astChannels[eTxPath].iFd, pucTxMessage, ucMessageLength); + if (iResult < 0) { + ANT_ERROR("failed to write data message to device: %s", strerror(errno)); + } else if (iResult != ucMessageLength) { + ANT_ERROR("bytes written and message size don't match up"); + } else { + stTimeout.tv_sec = time(0) + ANT_FLOW_GO_WAIT_TIMEOUT_SEC; + stTimeout.tv_nsec = 0; + + while (stRxThreadInfo.astChannels[eFlowMessagePath].ucFlowControlResp != ANT_FLOW_GO) { + iCondWaitResult = pthread_cond_timedwait(&stFlowControlCond, &stFlowControlLock, &stTimeout); + if (iCondWaitResult) { + ANT_ERROR("failed to wait for flow control response: %s", strerror(iCondWaitResult)); + + if (iCondWaitResult == ETIMEDOUT) { + status = ANT_STATUS_HARDWARE_ERR; + +#ifdef ANT_FLOW_RESEND + // Clear Tx message so will stop resending it from Rx thread + stRxThreadInfo.astChannels[eFlowMessagePath].ucResendMessageLength = 0; + stRxThreadInfo.astChannels[eFlowMessagePath].pucResendMessage = NULL; +#endif // ANT_FLOW_RESEND + } + goto wait_error; + } + } + + status = ANT_STATUS_SUCCESS; + } + +wait_error: + ANT_DEBUG_V("releasing stFlowControlLock in %s", __FUNCTION__); + pthread_mutex_unlock(&stFlowControlLock); + ANT_DEBUG_V("released stFlowControlLock in %s", __FUNCTION__); + +out: + ANT_FUNC_END(); + return status; +} + +//////////////////////////////////////////////////////////////////// +// ant_tx_message_flowcontrol_none +// +// Sends an ANT message to the chip without waiting for flow control +// +// Parameters: +// eTxPath device to transmit on +// ucMessageLength the length of the message +// pucMesg pointer to the message data +// +// Returns: +// Success: +// ANT_STATUS_SUCCESS +// Failure: +// ANT_STATUS_NOT_ENABLED +// +// Psuedocode: +/* + WRITE txBuffer to Tx Path (only length of packet part) + IF Wrote less then 0 bytes + Log error + RESULT = FAILED + ELSE IF Didn't write 'length of packet' bytes + Log error + RESULT = FAILED + ELSE + RESULT = SUCCESS + ENDIF +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus ant_tx_message_flowcontrol_none(ant_channel_type eTxPath, ANT_U8 ucMessageLength, ANT_U8 *pucTxMessage) +{ + int iResult; + ANTStatus status = ANT_STATUS_FAILED;\ + ANT_FUNC_START(); + + iResult = write(stRxThreadInfo.astChannels[eTxPath].iFd, pucTxMessage, ucMessageLength); + if (iResult < 0) { + ANT_ERROR("failed to write message to device: %s", strerror(errno)); + } else if (iResult != ucMessageLength) { + ANT_ERROR("bytes written and message size don't match up"); + } else { + status = ANT_STATUS_SUCCESS; + } + + ANT_FUNC_END(); + return status; +} + +//////////////////////////////////////////////////////////////////// +// ant_tx_message +// +// Frames ANT data and decides which flow control method to use for sending the +// ANT message to the chip +// +// Parameters: +// ucLen the length of the message +// pucMesg pointer to the message data +// +// Returns: +// Success: +// ANT_STATUS_SUCCESS +// Failure: +// ANT_STATUS_NOT_ENABLED +// +// Psuedocode: +/* +IF not enabled + RESULT = BT NOT INITIALIZED +ELSE + Create txBuffer, MAX HCI Message Size large + PUT ucLen in txBuffer AT ANT HCI Size Offset (0) + COPY pucMesg to txBuffer AT ANT HCI Header Size (1) <- ? Not at offset? + LOG txBuffer as a serial Tx (only length of packet part) + IF is a data message + Tx message on Data Path with FLOW_GO/FLOW_STOP flow control (ant_tx_message_flowcontrol_go_stop()) + ELSE + Tx message on Command Path with no flow control (ant_tx_message_flowcontrol_none()) + ENDIF +ENDIF +*/ +//////////////////////////////////////////////////////////////////// +ANTStatus ant_tx_message(ANT_U8 ucLen, ANT_U8 *pucMesg) +{ + // During a tx we must prepend a packet type byte. Thus HCI_PACKET_TYPE_SIZE is added + // to all offsets when writing into the tx buffer. + ANTStatus status = ANT_STATUS_FAILED; + // TODO ANT_HCI_MAX_MSG_SIZE is transport (driver) dependent. + ANT_U8 txBuffer[HCI_PACKET_TYPE_SIZE + ANT_HCI_MAX_MSG_SIZE]; + // TODO Message length can be greater than ANT_U8 can hold. + // Not changed as ANT_SERIAL takes length as ANT_U8. + ANT_U8 txMessageLength = HCI_PACKET_TYPE_SIZE + ucLen + ANT_HCI_HEADER_SIZE; + ANT_FUNC_START(); + + if (ant_radio_enabled_status() != RADIO_STATUS_ENABLED) { + status = ANT_STATUS_FAILED_BT_NOT_INITIALIZED; + goto out; + } + +#if ANT_HCI_OPCODE_SIZE == 1 + txBuffer[HCI_PACKET_TYPE_SIZE + ANT_HCI_OPCODE_OFFSET] = ANT_HCI_OPCODE_TX; +#elif ANT_HCI_OPCODE_SIZE > 1 +#error "Specified ANT_HCI_OPCODE_SIZE not currently supported" +#endif + +#if ANT_HCI_SIZE_SIZE == 1 + txBuffer[HCI_PACKET_TYPE_SIZE + ANT_HCI_SIZE_OFFSET] = ucLen; +#elif ANT_HCI_SIZE_SIZE == 2 + ANT_UTILS_StoreLE16(txBuffer + HCI_PACKET_TYPE_SIZE + ANT_HCI_SIZE_OFFSET, (ANT_U16)ucLen); +#else +#error "Specified ANT_HCI_SIZE_SIZE not currently supported" +#endif + + memcpy(txBuffer + HCI_PACKET_TYPE_SIZE + ANT_HCI_HEADER_SIZE, pucMesg, ucLen); + +// We no longer do the serial logging here because the packet type byte is not yet written. +//ANT_SERIAL(txBuffer, txMessageLength, 'T'); + +// We only do this if we are using single physical and logical channels. +#if defined(ANT_DEVICE_NAME) && (HCI_PACKET_TYPE_SIZE == 0) // Single transport path + ANT_SERIAL(txBuffer, txMessageLength, 'T'); + status = ant_tx_message_flowcontrol_wait(SINGLE_CHANNEL, SINGLE_CHANNEL, txMessageLength, txBuffer); +#else // Separate data/command paths + // Each path follows this structure: + // write the packet type if needed. + // log the packet + // Send using the appropriate physical channel, waiting for flow control for data commands. + switch (txBuffer[HCI_PACKET_TYPE_SIZE + ANT_HCI_DATA_OFFSET + ANT_MSG_ID_OFFSET]) { + case MESG_BROADCAST_DATA_ID: + case MESG_ACKNOWLEDGED_DATA_ID: + case MESG_BURST_DATA_ID: + case MESG_EXT_BROADCAST_DATA_ID: + case MESG_EXT_ACKNOWLEDGED_DATA_ID: + case MESG_EXT_BURST_DATA_ID: + case MESG_ADV_BURST_DATA_ID: + ANT_DEBUG_V("Data Path"); + #if HCI_PACKET_TYPE_SIZE == 1 + txBuffer[0] = ANT_DATA_TYPE_PACKET; + #elif HCI_PACKET_TYPE_SIZE > 1 + #error "Specified HCI_PACKET_TYPE_SIZE not supported" + #endif + ANT_SERIAL(txBuffer, txMessageLength, 'T'); + #ifdef ANT_DEVICE_NAME + status = ant_tx_message_flowcontrol_wait(SINGLE_CHANNEL, SINGLE_CHANNEL, txMessageLength, txBuffer); + #else + status = ant_tx_message_flowcontrol_wait(DATA_CHANNEL, COMMAND_CHANNEL, txMessageLength, txBuffer); + #endif + break; + default: + ANT_DEBUG_V("Control Path"); + #if HCI_PACKET_TYPE_SIZE == 1 + txBuffer[0] = ANT_CMD_TYPE_PACKET; + #elif HCI_PACKET_TYPE_SIZE > 1 + #error "Specified HCI_PACKET_TYPE_SIZE not supported" + #endif + ANT_SERIAL(txBuffer, txMessageLength, 'T'); + #ifdef ANT_DEVICE_NAME + status = ant_tx_message_flowcontrol_none(SINGLE_CHANNEL, txMessageLength, txBuffer); + #else + status = ant_tx_message_flowcontrol_none(COMMAND_CHANNEL, txMessageLength, txBuffer); + #endif + } +#endif // Separate data/command paths + +out: + ANT_FUNC_END(); + return status; +} + +//----------------- TODO Move these somewhere for multi transport path / dedicated channel support: + +static void ant_channel_init(ant_channel_info_t *pstChnlInfo, const char *pcCharDevName) +{ + ANT_FUNC_START(); + + // TODO Don't need to store, only accessed when trying to open: + // Is however useful for logs. + pstChnlInfo->pcDevicePath = pcCharDevName; + + // This is the only piece of info that needs to be stored per channel + pstChnlInfo->iFd = -1; + + // TODO Only 1 of these (not per-channel) is actually ever used: + pstChnlInfo->fnRxCallback = NULL; + pstChnlInfo->ucFlowControlResp = ANT_FLOW_GO; +#ifdef ANT_FLOW_RESEND + pstChnlInfo->ucResendMessageLength = 0; + pstChnlInfo->pucResendMessage = NULL; +#endif // ANT_FLOW_RESEND + // TODO Only used when Flow Control message received, so must only be Command path Rx thread + pstChnlInfo->pstFlowControlCond = &stFlowControlCond; + pstChnlInfo->pstFlowControlLock = &stFlowControlLock; + + ANT_FUNC_END(); +} + +// This function is used as an alternative to opening the char device directly. +// It is needed as libbt-vendor does the power up/down control for us when we open/close the file descriptor. +int init_transport_bdroid(int on) { + + void *so_handle; + unsigned char bdaddr[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + int fd[CH_MAX], powerstate, ret; + + if (on) { + so_handle = dlopen("libbt-vendor.so", RTLD_NOW); + if (!so_handle) + { + ALOGE("Failed to load vendor component"); + return -1; + } + + vendor_interface = (bt_vendor_interface_t *) dlsym(so_handle, "BLUETOOTH_VENDOR_LIB_INTERFACE"); + if (!vendor_interface) + { + ALOGE("Failed to accesst bt vendor interface"); + return -1; + } + + vendor_interface->init(&vendor_callbacks, bdaddr); + + ALOGI("Turn On BT power"); + powerstate = BT_VND_PWR_ON; + ret = vendor_interface->op(BT_VND_OP_POWER_CTRL, &powerstate); + if (ret < 0) + { + ALOGE("Failed to turn on power from bt vendor interface"); + return -1; + } + /*call ANT_USERIAL_OPEN to get ANT handle*/ + ret = vendor_interface->op(BT_VND_OP_ANT_USERIAL_OPEN, fd); + ALOGE("ret value: %d", ret); + if (ret != 1) + { + ALOGE("Failed to get fd from bt vendor interface"); + return -1; + } else { + ALOGE("FD: %x", fd[0]); + return fd[0]; + } + } else { + if (vendor_interface) { + ALOGE("Close and cleanup the interfaces"); + int ret = vendor_interface->op(BT_VND_OP_ANT_USERIAL_CLOSE, NULL); + + ALOGE("ret value: %d", ret); + ALOGI("Turn off BT power"); + powerstate = BT_VND_PWR_OFF; + ret = vendor_interface->op(BT_VND_OP_POWER_CTRL, &powerstate); + if (ret < 0) + { + ALOGE("Failed to turn off power from bt vendor interface"); + return -1; + } + vendor_interface->cleanup(); + vendor_interface = NULL; + return 0; + } else { + + ALOGE("Not able to find vendor interface handle"); + return -1; + } + } +} + +static void ant_disable_channel(ant_channel_info_t *pstChnlInfo) +{ + ANT_FUNC_START(); + if (!pstChnlInfo) { + ANT_ERROR("null channel info passed to channel disable function"); + goto out; + } + if (pstChnlInfo->iFd != -1) { + // Use the new init_transport function instead of open() to get our fd. + if (init_transport_bdroid(0) < 0) { + ANT_ERROR("failed to close channel %s(%#x): %s", pstChnlInfo->pcDevicePath, pstChnlInfo->iFd, strerror(errno)); + } + + pstChnlInfo->iFd = -1; //TODO can this overwrite a still valid fd? + } else { + ANT_DEBUG_D("%s file is already closed", pstChnlInfo->pcDevicePath); + } + +out: + ANT_FUNC_END(); +} + +static int ant_enable_channel(ant_channel_info_t *pstChnlInfo) +{ + int iRet = -1; + ANT_FUNC_START(); + if (!pstChnlInfo) { + ANT_ERROR("null channel info passed to channel enable function"); + errno = EINVAL; + goto out; + } + if (pstChnlInfo->iFd == -1) { + // Use the init_transport function to release our fd instead of close() + pstChnlInfo->iFd = init_transport_bdroid(1); + if (pstChnlInfo->iFd < 0) { + ANT_ERROR("failed to open dev %s: %s", pstChnlInfo->pcDevicePath, strerror(errno)); + goto out; + } + } else { + ANT_DEBUG_D("%s is already enabled", pstChnlInfo->pcDevicePath); + } + iRet = 0; +out: + ANT_FUNC_END(); + return iRet; +} + +//----------------------------------------------------------------------- This is antradio_power.h: + +int ant_enable(void) +{ + int iRet = -1; + ant_channel_type eChannel; + ANT_FUNC_START(); + + // Reset the shutdown signal. + uint64_t counter; + ssize_t result = read(stRxThreadInfo.iRxShutdownEventFd, &counter, sizeof(counter)); + // EAGAIN result indicates that the counter was already 0 in non-blocking mode. + if(result < 0 && errno != EAGAIN) + { + ANT_ERROR("Could not clear shutdown signal in enable. Reason: %s", strerror(errno)); + goto out; + } + + stRxThreadInfo.ucRunThread = 1; + + for (eChannel = 0; eChannel < NUM_ANT_CHANNELS; eChannel++) { + if (ant_enable_channel(&stRxThreadInfo.astChannels[eChannel]) < 0) { + ANT_ERROR("failed to enable channel %s: %s", + stRxThreadInfo.astChannels[eChannel].pcDevicePath, + strerror(errno)); + goto out; + } + } + + if (stRxThreadInfo.stRxThread == 0) { + if (pthread_create(&stRxThreadInfo.stRxThread, NULL, fnRxThread, &stRxThreadInfo) < 0) { + ANT_ERROR("failed to start rx thread: %s", strerror(errno)); + goto out; + } + } else { + ANT_DEBUG_D("rx thread is already running"); + } + + if (!stRxThreadInfo.ucRunThread) { + ANT_ERROR("rx thread crashed during init"); + goto out; + } + + iRet = 0; + +out: + ANT_FUNC_END(); + return iRet; +} + +int ant_disable(void) +{ + int iRet = -1; + ant_channel_type eChannel; + ANT_FUNC_START(); + + stRxThreadInfo.ucRunThread = 0; + + if (stRxThreadInfo.stRxThread != 0) { + ANT_DEBUG_I("Sending shutdown signal to rx thread."); + if(write(stRxThreadInfo.iRxShutdownEventFd, &EVENT_FD_PLUS_ONE, sizeof(EVENT_FD_PLUS_ONE)) < 0) + { + ANT_ERROR("failed to signal rx thread with eventfd. Reason: %s", strerror(errno)); + goto out; + } + ANT_DEBUG_I("Waiting for rx thread to finish."); + if (pthread_join(stRxThreadInfo.stRxThread, NULL) < 0) { + ANT_ERROR("failed to join rx thread: %s", strerror(errno)); + goto out; + } + } else { + ANT_DEBUG_D("rx thread is not running"); + } + + for (eChannel = 0; eChannel < NUM_ANT_CHANNELS; eChannel++) { + ant_disable_channel(&stRxThreadInfo.astChannels[eChannel]); + } + + iRet = 0; + +out: + stRxThreadInfo.stRxThread = 0; + ANT_FUNC_END(); + return iRet; +} + +//--------------------------------------------------------- + +const char *ant_get_lib_version() +{ + return "libantradio.so: "ANT_CHIP_NAME". Version " + LIBANT_STACK_MAJOR"."LIBANT_STACK_MINOR"."LIBANT_STACK_INCRE; +} |