blob: 3894cd6c5609fc3a4b0bfa02eba96dd95c20e14c [file] [log] [blame]
/*
* ANT Stack
*
* Copyright 2009 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_rx.c
*
* BRIEF:
* This file implements the receive thread for a BlueZ HCI implementation
* using Vendor Specific messages.
*
*
\******************************************************************************/
#define _GNU_SOURCE /* needed for PTHREAD_MUTEX_RECURSIVE */
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include "ant_types.h"
#if USE_EXTERNAL_POWER_LIBRARY
#include "antradio_power.h"
#endif
#include "ant_rx.h"
#include "ant_hciutils.h"
#include "ant_framing.h"
#include "ant_log.h"
#undef LOG_TAG
#define LOG_TAG "antradio_rx"
/* Global Options */
ANTHCIRxParams RxParams = {
.pfRxCallback = NULL,
.pfStateCallback = NULL,
.thread = 0
};
extern pthread_mutex_t enableLock;
extern ANTRadioEnabledStatus get_and_set_radio_status(void);
#ifndef USE_EXTERNAL_POWER_LIBRARY
extern ANTRadioEnabledStatus radio_status;
#endif
/*
* This thread opens a Bluez HCI socket and waits for ANT messages.
*/
void *ANTHCIRxThread(void *pvHCIDevice)
{
int ret = ANT_STATUS_SUCCESS;
int rxSocket;
int len;
unsigned char buf[HCI_MAX_EVENT_SIZE];
int result;
struct hci_filter eventVendorFilter;
ANT_FUNC_START();
(void)pvHCIDevice; //unused waring
ANT_DEBUG_D("Entering ANTHCIRxThread");
rxSocket = create_hci_sock();
if (rxSocket < 0)
{
ANT_DEBUG_E("can't open HCI socket in rx thread: %s", strerror(errno));
ret = ANT_STATUS_FAILED;
goto out;
}
eventVendorFilter.type_mask = TYPE_MASK_EVENT_PACKET;
eventVendorFilter.event_mask[0] = 0;
eventVendorFilter.event_mask[1] = EVENT_MASK_1_EVENT_VENDOR;
eventVendorFilter.opcode = htobs(ANT_EVENT_VENDOR_CODE);
if (setsockopt(rxSocket, SOL_HCI, HCI_FILTER, &eventVendorFilter, sizeof(eventVendorFilter)) < 0)
{
ANT_ERROR("failed to set socket options: %s", strerror(errno));
ret = ANT_STATUS_FAILED;
goto close;
}
/* continue running as long as not terminated */
while (get_and_set_radio_status() == RADIO_STATUS_ENABLED)
{
struct pollfd p;
int n;
p.fd = rxSocket;
p.events = POLLIN;
ANT_DEBUG_V(" RX: Polling HCI for data...");
/* poll socket, wait for ANT messages */
while ((n = poll(&p, 1, 2500)) == -1)
{
if (errno == EAGAIN || errno == EINTR)
continue;
ANT_ERROR("failed to poll socket: %s", strerror(errno));
ret = ANT_STATUS_FAILED;
goto close;
}
/* we timeout once in a while */
/* this let's us the chance to check if we were terminated */
if (0 == n)
{
ANT_DEBUG_V(" RX: Timeout");
continue;
}
ANT_DEBUG_D("New HCI data available, reading...");
/* read newly arrived data */
/* TBD: rethink assumption about single arrival */
while ((len = read(rxSocket, buf, sizeof(buf))) < 0)
{
if (errno == EAGAIN || errno == EINTR)
continue;
ANT_ERROR("failed to read socket: %s", strerror(errno));
ret = ANT_STATUS_FAILED;
goto close;
}
hci_event_packet_t *event_packet = (hci_event_packet_t *)buf;
int hci_payload_len = validate_hci_event_packet(event_packet, len);
if (hci_payload_len == -1)
{
// part of the message is incorrect, ignore it. validate_event_packet will log error
continue;
}
ANT_SERIAL(event_packet->hci_payload, hci_payload_len, 'R');
if(RxParams.pfRxCallback != NULL)
{
RxParams.pfRxCallback(hci_payload_len, event_packet->hci_payload);
}
else
{
ANT_ERROR("Can't send rx message - no callback registered");
}
}
close:
result = pthread_mutex_trylock(&enableLock);
ANT_DEBUG_D("rx thread close: trylock enableLock returned %d", result);
if (result == 0)
{
ANT_DEBUG_W("rx thread socket has unexpectedly crashed");
#if USE_EXTERNAL_POWER_LIBRARY
if (RxParams.pfStateCallback)
RxParams.pfStateCallback(RADIO_STATUS_DISABLING);
ant_disable();
get_and_set_radio_status();
#else
radio_status = RADIO_STATUS_DISABLED;
#endif
RxParams.thread = 0;
pthread_mutex_unlock(&enableLock);
}
else if (result == EBUSY)
{
ANT_DEBUG_V("rx thread socket was closed");
}
else
{
ANT_ERROR("rx thread close: trylock failed: %s", strerror(result));
}
if (-1 == close(rxSocket))
{
ANT_ERROR("failed to close hci device (socket handle=%#x): %s", rxSocket, strerror(errno));
}
else
{
ANT_DEBUG_D("closed hci device (socket handle=%#x)", rxSocket);
}
out:
ANT_FUNC_END();
pthread_exit((void *)ret);
#if defined(ANDROID)
return 0;
#endif
}