blob: 759b385f58929978e65af69faf8657985250cb27 [file] [log] [blame]
/*
* 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 <string.h>
#include <fcntl.h> /* for open() */
#include <linux/ioctl.h> /* For hard reset */
#include <pthread.h>
#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"
#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)
{
ANTStatus status = ANT_STATUS_FAILED;
// TODO ANT_HCI_MAX_MSG_SIZE is transport (driver) dependent.
ANT_U8 txBuffer[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 = 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[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[ANT_HCI_SIZE_OFFSET] = ucLen;
#elif ANT_HCI_SIZE_SIZE == 2
ANT_UTILS_StoreLE16(txBuffer + ANT_HCI_SIZE_OFFSET, (ANT_U16)ucLen);
#else
#error "Specified ANT_HCI_SIZE_SIZE not currently supported"
#endif
memcpy(txBuffer + ANT_HCI_HEADER_SIZE, pucMesg, ucLen);
ANT_SERIAL(txBuffer, txMessageLength, 'T');
#ifdef ANT_DEVICE_NAME // Single transport path
status = ant_tx_message_flowcontrol_wait(SINGLE_CHANNEL, SINGLE_CHANNEL, txMessageLength, txBuffer);
#else // Separate data/command paths
switch (txBuffer[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:
status = ant_tx_message_flowcontrol_wait(DATA_CHANNEL, COMMAND_CHANNEL, txMessageLength, txBuffer);
break;
default:
status = ant_tx_message_flowcontrol_none(COMMAND_CHANNEL, txMessageLength, txBuffer);
}
#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();
}
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) {
if (close(pstChnlInfo->iFd) < 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) {
pstChnlInfo->iFd = open(pstChnlInfo->pcDevicePath, O_RDWR);
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;
}