blob: e1efa7fd2558c40465937b08e2fd12047ea09466 [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_tx.c
*
* BRIEF:
* This file Implements the transmit functionality for a BlueZ HCI
* implementation using Vendor Specific messages.
*
*
\******************************************************************************/
#include <errno.h>
#include <poll.h>
#include <sys/uio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include "ant_types.h"
#include "ant_hciutils.h"
#include "ant_framing.h"
#include "ant_utils.h"
#include "ant_log.h"
#undef LOG_TAG
#define LOG_TAG "antradio_tx"
int g_ant_cmd_socket = -1;
int ant_open_tx_transport(void)
{
int socket = -1;
struct hci_filter commandCompleteFilter;
ANT_FUNC_START();
socket = create_hci_sock();
if (socket < 0)
{
ANT_DEBUG_E("failed to open HCI socket for tx: %s", strerror(errno));
}
else
{
g_ant_cmd_socket = socket;
ANT_DEBUG_D("socket handle %#x", socket);
commandCompleteFilter.type_mask = TYPE_MASK_EVENT_PACKET;
commandCompleteFilter.event_mask[0] = EVENT_MASK_0_COMMAND_COMPLETE;
commandCompleteFilter.event_mask[1] = 0;
commandCompleteFilter.opcode = htobs(ANT_COMMAND_OPCODE);
if (setsockopt(socket, SOL_HCI, HCI_FILTER, &commandCompleteFilter, sizeof(commandCompleteFilter)) < 0)
{
ANT_ERROR("failed to set socket options: %s", strerror(errno));
close(socket);
socket = -1;
}
}
ANT_FUNC_END();
return socket;
}
void ant_close_tx_transport(int socket)
{
ANT_FUNC_START();
if(0 < socket)
{
if (0 == close(socket))
{
ANT_DEBUG_D("closed hci device (socket handle=%#x)", socket);
}
else
{
ANT_ERROR("failed to close hci device (socket handle=%#x): %s", socket, strerror(errno));
}
}
else
{
ANT_DEBUG_E("requested close on socket %#x. invalid param", socket);
}
ANT_FUNC_END();
}
/*
Format of an HCI WRITE command to ANT chip:
HCI Header:
----------
- HCI Packet Type: 1 byte
- HCI Opcode: 2 bytes
(LSB, MSB - LE)
- HCI Parameters Total Len (total length of all subsequent fields): 1 byte
HCI Parameters:
--------------
- VS Parameters Len (total length of ANT Mesg inculding Len/ID) 2 bytes
(LSB, MSB - LE)
- ANT Mesg Len (N = number of bytes in ANT Mesg Data): 1 byte
- ANT Mesg ID: 1 byte
- ANT Mesg Data: N bytes
*/
ANT_BOOL wait_for_message(int socket)
{
struct pollfd p;
int n;
ANT_BOOL bReturn = ANT_FALSE;
ANT_BOOL bRetry = ANT_FALSE;
ANT_FUNC_START();
p.fd = socket;
p.events = POLLIN;
do
{
bRetry = ANT_FALSE;
ANT_DEBUG_V(" CC: Polling HCI for data...");
/* poll socket, wait for ANT messages */
n = poll(&p, 1, 2500);
if (0 > n)
{
if (errno == EAGAIN || errno == EINTR)
{
ANT_DEBUG_W(" CC: error: %s", strerror(errno));
bRetry = ANT_TRUE;
}
else
{
ANT_ERROR("failed to poll socket. error: %s", strerror(errno));
}
}
/* timeout */
else if (0 == n)
{
ANT_ERROR("SERIOUS: Timeouted getting Command Complete");
}
else if(0 < n)
{
// There is data to read.
bReturn = ANT_TRUE;
}
} while(ANT_TRUE == bRetry);
ANT_FUNC_END();
return bReturn;
}
ANTStatus write_data(ANT_U8 ant_message[], int ant_message_len)
{
ANTStatus ret = ANT_STATUS_FAILED;
ANT_BOOL retry = ANT_FALSE;
int bytes_written;
struct iovec iov[2];
hci_command_vendor_header_t hci_header;
ANT_FUNC_START();
create_command_header(&hci_header, ant_message_len);
iov[0].iov_base = &hci_header;
iov[0].iov_len = sizeof(hci_header);
iov[1].iov_base = ant_message;
iov[1].iov_len = ant_message_len;
do //while retry
{
retry = ANT_FALSE;
if (g_ant_cmd_socket < 0)
{
ANT_DEBUG_E("bad socket handle %#x", g_ant_cmd_socket);
return ANT_STATUS_INTERNAL_ERROR;
}
ANT_SERIAL(ant_message, ant_message_len, 'T');
bytes_written = writev(g_ant_cmd_socket, iov, 2);
ANT_DEBUG_D("writing to socket %#x returned %d", g_ant_cmd_socket,
bytes_written);
// bytes_written < 0 = error (check errno)
// bytes_written = 0 = No data written
// bytes_written < sizeof(hci_message) = not all data written
// bytes_written = sizeof(hci_message) = all data written
if(bytes_written < 0)
{
ANT_ERROR("write to HCI failed: %s", strerror(errno));
if (errno == EAGAIN || errno == EINTR)
{
ANT_DEBUG_D("Retrying write to HCI");
retry = ANT_TRUE;
}
else
{
ret = ANT_STATUS_FAILED;
}
}
else if(bytes_written < ((int)sizeof(hci_header) + ant_message_len))
{
ANT_DEBUG_D("Only %d bytes written to HCI.", bytes_written);
ret = ANT_STATUS_FAILED;
}
else
{
ANT_DEBUG_V("writev successful");
ret = ANT_STATUS_SUCCESS;
}
} while(retry);
ANT_FUNC_END();
return ret;
}
// Returns:
// ANT_STATUS_NO_VALUE_AVAILABLE if not a CC packet
// ANT_STATUS_FAILED if could not read socket or not a
// valid length CC packet
// ANT_STATUS_TRANSPORT_UNSPECIFIED_ERROR if CC indicates an unspecified error
// ANT_STATUS_COMMAND_WRITE_FAILED if CC indicates a failure
// ANT_STATUS_SUCCESS if CC indicates message was received
ANTStatus get_command_complete_result(int socket)
{
ANTStatus status = ANT_STATUS_NO_VALUE_AVAILABLE;
int len;
ANT_U8 ucResult = -1;
ANT_U8 buf[HCI_MAX_EVENT_SIZE];
ANT_FUNC_START();
ANT_DEBUG_V("reading off socket %#x", socket);
/* read newly arrived data */
while ((len = read(socket, buf, sizeof(buf))) < 0)
{
if (errno == EAGAIN || errno == EINTR)
continue;
ANT_ERROR("failed to read socket. error: %s", strerror(errno));
status = ANT_STATUS_FAILED;
goto close;
}
ANT_SERIAL(buf, len, 'C');
// validate that we have a single command complete packet
if (len != sizeof(hci_command_complete_packet_t))
{
status = ANT_STATUS_FAILED;
}
else
{
hci_command_complete_packet_t *command_complete = (hci_command_complete_packet_t *)buf;
if(command_complete->packet_type == HCI_EVENT_PKT)
{
ANT_DEBUG_D("Received Event Packet");
if(command_complete->event_header.evt == EVT_CMD_COMPLETE)
{
ANT_U16 opcode = ANT_UTILS_LEtoHost16((ANT_U8 *)&command_complete->command_complete_hdr.opcode);
if(opcode == ANT_COMMAND_OPCODE)
{
ANT_DEBUG_V("Received COMMAND COMPLETE");
ucResult = command_complete->response;
if(ucResult == 0)
{
ANT_DEBUG_D("Command Complete = SUCCESS");
status = ANT_STATUS_SUCCESS;
}
else if(ucResult == HCI_UNSPECIFIED_ERROR)
{
ANT_DEBUG_D("Command Complete = UNSPECIFIED_ERROR");
status = ANT_STATUS_TRANSPORT_UNSPECIFIED_ERROR;
}
else
{
status = ANT_STATUS_COMMAND_WRITE_FAILED;
ANT_DEBUG_D("Command Complete = WRITE_FAILED");
}
}
else
{
ANT_DEBUG_W("Command complete has wrong opcode, this should have been filtered out");
}
}
else
{
ANT_DEBUG_W("Event is not a command complete, this should have been filtered out");
}
}
else
{
ANT_DEBUG_W("Other Event Packet, this should have been filtered out");
}
}
close:
ANT_FUNC_END();
return status;
}