change(bt/bluedroid): Refactored HID host datapath

This commit is contained in:
liqigan
2026-06-11 19:55:15 +08:00
committed by BOT
parent da8bc0afb5
commit e47f74ffc0
7 changed files with 672 additions and 137 deletions

View File

@@ -40,7 +40,6 @@
** Constants
*****************************************************************************/
/*****************************************************************************
** Local Function prototypes
*****************************************************************************/
@@ -96,6 +95,7 @@ void bta_hh_api_enable(tBTA_HH_DATA *p_data)
for (xx = 0; xx < BTA_HH_MAX_KNOWN; xx ++) {
bta_hh_cb.cb_index[xx] = BTA_HH_IDX_INVALID;
}
}
#if (BTA_HH_LE_INCLUDED == TRUE)
@@ -538,8 +538,23 @@ void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
bta_hh_cb.cnt_num ++;
/* initialize device driver */
bta_hh_co_open(dev_handle, p_cb->sub_class,
p_cb->attr_mask, p_cb->app_id);
if (!bta_hh_co_open(dev_handle, p_cb->sub_class,
p_cb->attr_mask, p_cb->app_id)) {
conn.status = BTA_HH_ERR_NO_RES;
p_cb->opened = FALSE;
#if (BTA_HH_LE_INCLUDED == TRUE)
if (!p_cb->is_le_device)
#endif
{
/* Balance the async close path which unconditionally calls bta_sys_conn_close(). */
bta_sys_conn_open(BTA_ID_HH, p_cb->app_id, p_cb->addr);
}
HID_HostCloseDev(dev_handle);
(* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn);
p_cb->incoming_conn = FALSE;
p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE;
return;
}
#if (BTA_HH_LE_INCLUDED == TRUE)
conn.status = p_cb->status;
@@ -552,6 +567,7 @@ void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
/* inform role manager */
bta_sys_conn_open( BTA_ID_HH , p_cb->app_id, p_cb->addr);
}
p_cb->opened = TRUE;
/* set protocol mode when not default report mode */
if ( p_cb->mode != BTA_HH_PROTO_RPT_MODE
#if (BTA_HH_LE_INCLUDED == TRUE)
@@ -631,12 +647,15 @@ void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
void bta_hh_data_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
{
BT_HDR *pdata = p_data->hid_cback.p_data;
UINT8 *p_rpt = (UINT8 *)(pdata + 1) + pdata->offset;
bta_hh_co_data((UINT8)p_data->hid_cback.hdr.layer_specific, p_rpt, pdata->len,
p_cb->mode, p_cb->sub_class, p_cb->dscp_info.ctry_code, p_cb->addr, p_cb->app_id);
if (pdata == NULL) {
return;
}
utl_freebuf((void **)&pdata);
bta_hh_co_data_hdr((UINT8)p_data->hid_cback.hdr.layer_specific, pdata,
p_cb->mode, p_cb->sub_class, p_cb->dscp_info.ctry_code,
p_cb->addr, p_cb->app_id);
p_data->hid_cback.p_data = NULL;
}
@@ -834,6 +853,7 @@ void bta_hh_open_failure(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
/* Report OPEN fail event */
(*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat);
p_cb->opened = FALSE;
#if BTA_HH_DEBUG
bta_hh_trace_dev_db();
@@ -885,6 +905,7 @@ void bta_hh_close_act (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
/* Report OPEN fail event */
(*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat);
p_cb->opened = FALSE;
#if BTA_HH_DEBUG
bta_hh_trace_dev_db();
@@ -893,24 +914,27 @@ void bta_hh_close_act (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
}
/* otherwise report CLOSE/VC_UNPLUG event */
else {
/* finaliza device driver */
bta_hh_co_close(p_cb->hid_handle, p_cb->app_id);
/* inform role manager */
bta_sys_conn_close( BTA_ID_HH , p_cb->app_id, p_cb->addr);
/* update total conn number */
bta_hh_cb.cnt_num --;
if (disc_dat.status) {
disc_dat.status = BTA_HH_ERR;
}
if (p_cb->opened) {
/* finalize device driver only for successfully opened devices */
bta_hh_co_close(p_cb->hid_handle, p_cb->app_id);
if (disc_dat.status) {
disc_dat.status = BTA_HH_ERR;
}
(*bta_hh_cb.p_cback)(event, (tBTA_HH *)&disc_dat);
(*bta_hh_cb.p_cback)(event, (tBTA_HH *)&disc_dat);
/* if virtually unplug, remove device */
if (p_cb->vp ) {
HID_HostRemoveDev( p_cb->hid_handle);
bta_hh_clean_up_kdev(p_cb);
/* if virtually unplug, remove device */
if (p_cb->vp ) {
HID_HostRemoveDev( p_cb->hid_handle);
bta_hh_clean_up_kdev(p_cb);
}
}
p_cb->opened = FALSE;
#if BTA_HH_DEBUG
bta_hh_trace_dev_db();
@@ -1190,9 +1214,17 @@ static void bta_hh_cback (UINT8 dev_handle, BD_ADDR addr, UINT8 event,
case HID_HDEV_EVT_CLOSE:
sm_event = BTA_HH_INT_CLOSE_EVT;
break;
case HID_HDEV_EVT_INTR_DATA:
sm_event = BTA_HH_INT_DATA_EVT;
break;
case HID_HDEV_EVT_INTR_DATA: {
UINT8 index = bta_hh_dev_handle_to_cb_idx(dev_handle);
tBTA_HH_DEV_CB *p_cb = (index != BTA_HH_IDX_INVALID) ? &bta_hh_cb.kdev[index] : NULL;
if (p_cb != NULL && p_cb->state == BTA_HH_CONN_ST) {
bta_hh_co_data_hdr(dev_handle, pdata, p_cb->mode, p_cb->sub_class, p_cb->dscp_info.ctry_code, p_cb->addr,
p_cb->app_id);
} else {
utl_freebuf((void **)&pdata);
}
return;
}
case HID_HDEV_EVT_HANDSHAKE:
sm_event = BTA_HH_INT_HANDSK_EVT;
break;

View File

@@ -2266,17 +2266,12 @@ void bta_hh_le_input_rpt_notify(tBTA_GATTC_NOTIFY *p_data)
p_buf = p_data->value;
}
bta_hh_co_data((UINT8)p_dev_cb->hid_handle,
p_buf,
p_data->len,
p_dev_cb->mode,
0 , /* no sub class*/
p_dev_cb->dscp_info.ctry_code,
p_dev_cb->addr,
app_id);
if (p_buf != p_data->value) {
osi_free(p_buf);
if (p_buf == p_data->value) {
bta_hh_le_co_data((UINT8)p_dev_cb->hid_handle, p_buf, p_data->len, p_dev_cb->mode, 0, /* no sub class*/
p_dev_cb->dscp_info.ctry_code, p_dev_cb->addr, app_id);
} else {
bta_hh_le_co_data_owned((UINT8)p_dev_cb->hid_handle, p_buf, p_data->len, p_dev_cb->mode, 0, /* no sub class*/
p_dev_cb->dscp_info.ctry_code, p_dev_cb->addr, app_id);
}
}

View File

@@ -28,6 +28,8 @@
#include "bta/bta_sys.h"
#include "bta/utl.h"
#include "bta/bta_hh_api.h"
#include "osi/pkt_queue.h"
#include "osi/thread.h"
//#if BTA_HH_LE_INCLUDED == TRUE
#include "bta/bta_gatt_api.h"
@@ -82,7 +84,7 @@ typedef UINT16 tBTA_HH_INT_EVT; /* HID host internal events */
#define BTA_HH_FST_TRANS_CB_EVT BTA_HH_GET_RPT_EVT
#define BTA_HH_FST_BTE_TRANS_EVT HID_TRANS_GET_REPORT
/* sub event code used for device maintainence API call */
/* sub event code used for device maintenance API call */
#define BTA_HH_ADD_DEV 0
#define BTA_HH_REMOVE_DEV 1
@@ -201,7 +203,7 @@ typedef struct {
#define BTA_HH_LE_PROTO_MODE_BIT 0x01
#define BTA_HH_LE_CP_BIT 0x02
UINT8 option_char; /* control point char exisit or not */
UINT8 option_char; /* control point char exist or not */
BOOLEAN expl_incl_srvc;
UINT8 incl_srvc_inst; /* assuming only one included service : battery service */

View File

@@ -25,6 +25,10 @@
#define BTA_HH_CO_H
#include "bta/bta_hh_api.h"
#include "osi/pkt_queue.h"
#include "stack/bt_types.h"
#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE)
typedef struct {
UINT16 rpt_uuid;
@@ -34,21 +38,48 @@ typedef struct {
UINT8 prop;
} tBTA_HH_RPT_CACHE_ENTRY;
typedef enum {
BTA_HH_DATA_BUF_RAW = 0,
BTA_HH_DATA_BUF_BT_HDR,
} tBTA_HH_DATA_BUF_TYPE;
typedef struct {
UINT8 dev_handle;
UINT8 buf_type; // see tBTA_HH_DATA_BUF_TYPE
tBTA_HH_PROTO_MODE proto_mode;
UINT16 len;
void *p_buf;
} tBTA_HH_DATA_PKT;
extern void bta_hh_co_data_pkt_free(tBTA_HH_DATA_PKT *pkt);
extern void bta_hh_co_data_linked_pkt_free(pkt_linked_item_t *item);
extern UINT8 *bta_hh_co_data_pkt_get_payload(tBTA_HH_DATA_PKT *pkt);
/*******************************************************************************
**
** Function bta_hh_co_data
**
** Description This callout function is executed by HH when data is received
** in interupt channel.
** in interrupt channel.
**
**
** Returns void.
**
*******************************************************************************/
extern void bta_hh_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len,
extern void bta_hh_co_data_hdr(UINT8 dev_handle, BT_HDR *p_hdr,
tBTA_HH_PROTO_MODE mode, UINT8 sub_class,
UINT8 ctry_code, BD_ADDR peer_addr, UINT8 app_id);
#if (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE)
extern void bta_hh_le_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len,
tBTA_HH_PROTO_MODE mode, UINT8 sub_class,
UINT8 ctry_code, BD_ADDR peer_addr, UINT8 app_id);
extern void bta_hh_le_co_data_owned(UINT8 dev_handle, UINT8 *p_buf, UINT16 len,
tBTA_HH_PROTO_MODE mode, UINT8 sub_class,
UINT8 ctry_code, BD_ADDR peer_addr, UINT8 app_id);
#endif /* (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE) */
/*******************************************************************************
**
** Function bta_hh_co_open
@@ -57,11 +88,11 @@ extern void bta_hh_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len,
** opened, and application may do some device specific
** initialization.
**
** Returns void.
** Returns TRUE if platform specific initialization succeeds.
**
*******************************************************************************/
extern void bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class,
UINT16 attr_mask, UINT8 app_id);
extern BOOLEAN bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class,
UINT16 attr_mask, UINT8 app_id);
/*******************************************************************************
**
@@ -129,4 +160,5 @@ extern tBTA_HH_RPT_CACHE_ENTRY *bta_hh_le_co_cache_load (BD_ADDR remote_bda,
extern void bta_hh_le_co_reset_rpt_cache (BD_ADDR remote_bda, UINT8 app_id);
#endif /* #if (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE) */
#endif /* defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) */
#endif /* BTA_HH_CO_H */

View File

@@ -1,8 +1,49 @@
#include <string.h>
#include "btc_hh.h"
#include "bta/utl.h"
#include "osi/allocator.h"
#if HID_HOST_INCLUDED == TRUE
void bta_hh_co_data_pkt_free(tBTA_HH_DATA_PKT *pkt)
{
if (pkt == NULL || pkt->p_buf == NULL) {
return;
}
if (pkt->buf_type == BTA_HH_DATA_BUF_BT_HDR) {
utl_freebuf((void **)&pkt->p_buf);
} else {
osi_free(pkt->p_buf);
}
pkt->p_buf = NULL;
}
void bta_hh_co_data_linked_pkt_free(pkt_linked_item_t *item)
{
if (item == NULL) {
return;
}
bta_hh_co_data_pkt_free((tBTA_HH_DATA_PKT *)item->data);
osi_free(item);
}
UINT8 *bta_hh_co_data_pkt_get_payload(tBTA_HH_DATA_PKT *pkt)
{
BT_HDR *hdr;
if (pkt == NULL || pkt->p_buf == NULL || pkt->len == 0) {
return NULL;
}
if (pkt->buf_type == BTA_HH_DATA_BUF_BT_HDR) {
hdr = (BT_HDR *)pkt->p_buf;
return hdr->data + hdr->offset;
}
return (UINT8 *)pkt->p_buf;
}
/*******************************************************************************
*
* Function bta_hh_co_open
@@ -10,16 +51,18 @@
* Description When connection is opened, this call-out function is executed
* by HH to do platform specific initialization.
*
* Returns void.
* Returns TRUE if platform specific initialization succeeds.
******************************************************************************/
void bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class, tBTA_HH_ATTR_MASK attr_mask, UINT8 app_id)
BOOLEAN bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class, tBTA_HH_ATTR_MASK attr_mask, UINT8 app_id)
{
BOOLEAN is_new_device = TRUE;
uint8_t old_dev_status = ESP_HIDH_CONN_STATE_UNKNOWN;
UINT32 i;
btc_hh_device_t *p_dev = NULL;
if (dev_handle == BTA_HH_INVALID_HANDLE) {
APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__, dev_handle);
return;
return FALSE;
}
for (i = 0; i < BTC_HH_MAX_HID; i++) {
@@ -30,6 +73,8 @@ void bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class, tBTA_HH_ATTR_MASK attr_ma
"dev_handle=0x%2x, attr_mask=0x%04x, sub_class=0x%02x, app_id=%d",
__func__, p_dev->dev_status, dev_handle, p_dev->attr_mask, p_dev->sub_class,
p_dev->app_id);
is_new_device = FALSE;
old_dev_status = p_dev->dev_status;
break;
}
p_dev = NULL;
@@ -54,11 +99,26 @@ void bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class, tBTA_HH_ATTR_MASK attr_ma
if (p_dev == NULL) {
APPL_TRACE_ERROR("%s: Error: too many HID devices are connected", __func__);
return;
return FALSE;
}
p_dev->dev_status = ESP_HIDH_CONN_STATE_CONNECTED;
if (!btc_hh_data_path_init(dev_handle)) {
APPL_TRACE_ERROR("%s: failed to init HID host datapath, dev_handle=%u", __func__, dev_handle);
if (is_new_device) {
p_dev->dev_status = ESP_HIDH_CONN_STATE_UNKNOWN;
p_dev->dev_handle = BTA_HH_INVALID_HANDLE;
if (btc_hh_cb.device_num) {
btc_hh_cb.device_num--;
}
} else {
p_dev->dev_status = old_dev_status;
}
return FALSE;
}
APPL_TRACE_DEBUG("%s: Return device status %d", __func__, p_dev->dev_status);
return TRUE;
}
/*******************************************************************************
@@ -93,64 +153,133 @@ void bta_hh_co_close(UINT8 dev_handle, UINT8 app_id)
break;
}
}
// data path will be cleaned up on CLOSE or UNPLUG event in btc context
}
/*******************************************************************************
*
* Function bta_hh_co_data
* Function bta_hh_co_data_hdr
*
* Description This function is executed by BTA when HID host receive a
* data report on interrupt channel.
*
* Parameters dev_handle - device handle
* *p_rpt - pointer to the report data
* len - length of report data
* mode - Hid host Protocol Mode
* sub_clas - Device Subclass
* app_id - application id
* Description Transfer BT_HDR ownership to BTC queue (BR/EDR zero-copy).
*
* Returns void
******************************************************************************/
void bta_hh_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len, tBTA_HH_PROTO_MODE mode, UINT8 sub_class, UINT8 ctry_code,
BD_ADDR peer_addr, UINT8 app_id)
void bta_hh_co_data_hdr(UINT8 dev_handle, BT_HDR *p_hdr, tBTA_HH_PROTO_MODE mode, UINT8 sub_class, UINT8 ctry_code,
BD_ADDR peer_addr, UINT8 app_id)
{
btc_msg_t msg;
tBTA_HH p_data;
BT_HDR *p_buf = NULL;
bt_status_t status;
tBTA_HH_STATUS ret = BTA_HH_OK;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_HH;
msg.act = BTA_HH_DATA_IND_EVT;
pkt_linked_item_t *linked_pkt = NULL;
tBTA_HH_DATA_PKT *pkt;
APPL_TRACE_DEBUG("%s: dev_handle = %d, subclass = 0x%02X, mode = %d, "
"ctry_code = %d, app_id = %d",
__func__, dev_handle, sub_class, mode, ctry_code, app_id);
do {
if ((p_rpt == NULL) || (len == 0)) {
ret = BTA_HH_ERR;
break;
}
if ((p_buf = osi_malloc(sizeof(BT_HDR) + len)) == NULL) {
APPL_TRACE_ERROR("%s malloc failed!", __func__);
ret = BTA_HH_ERR_NO_RES;
break;
}
p_buf->offset = 0;
p_buf->len = len;
p_buf->event = 0;
p_buf->layer_specific = dev_handle;
memcpy(p_buf->data, p_rpt, len);
} while (0);
if (p_hdr == NULL || p_hdr->len == 0) {
utl_freebuf((void **)&p_hdr);
return;
}
p_data.int_data.status = ret;
p_data.int_data.handle = dev_handle;
p_data.int_data.p_data = p_buf;
p_data.int_data.proto_mode = mode;
status = btc_transfer_context(&msg, &p_data, sizeof(tBTA_HH), NULL, NULL);
assert(status == BT_STATUS_SUCCESS);
linked_pkt = btc_hh_data_dequeue_reusable_linked_pkt(dev_handle);
if (linked_pkt != NULL) {
bta_hh_co_data_pkt_free((tBTA_HH_DATA_PKT *)linked_pkt->data);
} else {
linked_pkt = (pkt_linked_item_t *)osi_malloc(BT_PKT_LINKED_HDR_SIZE +
sizeof(tBTA_HH_DATA_PKT));
if (linked_pkt == NULL) {
btc_hh_on_pkt_dropped(dev_handle);
utl_freebuf((void **)&p_hdr);
return;
}
}
pkt = (tBTA_HH_DATA_PKT *)linked_pkt->data;
pkt->dev_handle = dev_handle;
pkt->len = p_hdr->len;
pkt->proto_mode = mode;
pkt->buf_type = BTA_HH_DATA_BUF_BT_HDR;
pkt->p_buf = p_hdr;
if (!btc_hh_data_enqueue_linked_pkt(linked_pkt)) {
btc_hh_on_pkt_dropped(dev_handle);
bta_hh_co_data_linked_pkt_free(linked_pkt);
}
}
#endif /* HID_HOST_INCLUDED == TRUE */
#if (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE)
/*******************************************************************************
*
* Function bta_hh_le_co_data_owned
*
* Description Transfer raw buffer ownership to BTC queue (BLE zero-copy).
*
* Returns void
******************************************************************************/
void bta_hh_le_co_data_owned(UINT8 dev_handle, UINT8 *p_buf, UINT16 len, tBTA_HH_PROTO_MODE mode, UINT8 sub_class,
UINT8 ctry_code, BD_ADDR peer_addr, UINT8 app_id)
{
tBTA_HH_DATA_PKT pkt = {
.dev_handle = dev_handle,
.len = len,
.proto_mode = mode,
.buf_type = BTA_HH_DATA_BUF_RAW,
.p_buf = p_buf,
};
APPL_TRACE_DEBUG("%s: dev_handle = %d, subclass = 0x%02X, mode = %d, "
"ctry_code = %d, app_id = %d",
__func__, dev_handle, sub_class, mode, ctry_code, app_id);
if ((p_buf == NULL) || (len == 0)) {
osi_free(p_buf);
return;
}
if (!btc_hh_data_enqueue_pkt(&pkt)) {
btc_hh_on_pkt_dropped(dev_handle);
osi_free(p_buf);
}
}
#endif /* HID_HOST_INCLUDED == TRUE */
/*******************************************************************************
*
* Function bta_hh_le_co_data
*
* Description Copy ephemeral report data into BTC queue (BLE GATT notify).
*
* Returns void
******************************************************************************/
void bta_hh_le_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len, tBTA_HH_PROTO_MODE mode, UINT8 sub_class,
UINT8 ctry_code, BD_ADDR peer_addr, UINT8 app_id)
{
UINT8 *p_buf;
tBTA_HH_DATA_PKT pkt;
APPL_TRACE_DEBUG("%s: dev_handle = %d, subclass = 0x%02X, mode = %d, "
"ctry_code = %d, app_id = %d",
__func__, dev_handle, sub_class, mode, ctry_code, app_id);
if ((p_rpt == NULL) || (len == 0)) {
return;
}
p_buf = (UINT8 *)osi_malloc(len);
if (p_buf == NULL) {
btc_hh_on_pkt_dropped(dev_handle);
return;
}
memcpy(p_buf, p_rpt, len);
pkt.dev_handle = dev_handle;
pkt.len = len;
pkt.proto_mode = mode;
pkt.buf_type = BTA_HH_DATA_BUF_RAW;
pkt.p_buf = p_buf;
if (!btc_hh_data_enqueue_pkt(&pkt)) {
btc_hh_on_pkt_dropped(dev_handle);
osi_free(p_buf);
}
}
#endif /* (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE) */

View File

@@ -34,9 +34,13 @@
#include "device/bdaddr.h"
#include "btc/btc_storage.h"
#include "osi/allocator.h"
#include "osi/pkt_queue.h"
#include "stack/bt_types.h"
#include "stack/gatt_api.h"
#include "bta/utl.h"
#include "bta/bta_hh_api.h"
#include "stack/l2c_api.h"
#include "esp_log.h"
// #include "bta_dm_int.h"
#if HID_HOST_INCLUDED == TRUE
@@ -64,7 +68,25 @@ static bdstr_t bdstr;
#define is_hidh_init() (btc_hh_cb.status > BTC_HH_DISABLED)
#define BTC_TIMEOUT_VUP_MS (3 * 1000)
#define BTC_HH_DATA_QUEUE_IDX (1)
/**
* Low-latency profile for gamepad-like traffic:
* keep queue short to avoid stale input backlog and keep batch moderate.
*/
#define BTC_HH_DATA_QUEUE_LEN_MAX (20) // for high report rate, the queue will cause 1.25ms * BTC_HH_DATA_QUEUE_LEN_MAX latency
#define BTC_HH_DATA_BATCH_SIZE_BASE (8)
#define BTC_HH_DATA_BATCH_SIZE_HIGH (10)
#define BTC_HH_DATA_BATCH_SIZE_MAX (12)
static void btc_hh_cb_arg_deep_free(btc_msg_t *msg);
static void btc_hh_data_pkt_handler(void *arg);
static size_t btc_hh_get_batch_size(size_t queue_len);
static bool btc_hh_data_path_init_inner(btc_hh_device_t *p_dev);
static void btc_hh_data_path_deinit_inner(btc_hh_device_t *p_dev);
static bool btc_hh_device_drop_cnt_init(btc_hh_device_t *p_dev);
static void btc_hh_device_drop_cnt_deinit(btc_hh_device_t *p_dev);
static void btc_hh_device_drop_cnt_set(btc_hh_device_t *p_dev, uint32_t cnt);
static void btc_hh_device_drop_cnt_inc(btc_hh_device_t *p_dev);
static inline void btc_hh_cb_to_app(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param)
{
@@ -146,6 +168,68 @@ btc_hh_device_t *btc_hh_find_connected_dev_by_handle(uint8_t handle)
return NULL;
}
static bool btc_hh_device_drop_cnt_init(btc_hh_device_t *p_dev)
{
if (p_dev == NULL) {
return false;
}
return (osi_mutex_new(&p_dev->lock) == 0);
}
static void btc_hh_device_drop_cnt_deinit(btc_hh_device_t *p_dev)
{
if (p_dev == NULL) {
return;
}
osi_mutex_free(&p_dev->lock);
}
static void btc_hh_device_drop_cnt_set(btc_hh_device_t *p_dev, uint32_t cnt)
{
if (p_dev == NULL) {
return;
}
osi_mutex_lock(&p_dev->lock, OSI_MUTEX_MAX_TIMEOUT);
p_dev->drop_pkt_cnt = cnt;
osi_mutex_unlock(&p_dev->lock);
}
static void btc_hh_device_drop_cnt_inc(btc_hh_device_t *p_dev)
{
if (p_dev == NULL) {
return;
}
osi_mutex_lock(&p_dev->lock, OSI_MUTEX_MAX_TIMEOUT);
p_dev->drop_pkt_cnt++;
osi_mutex_unlock(&p_dev->lock);
}
void btc_hh_on_pkt_dropped(uint8_t dev_handle)
{
btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_handle(dev_handle);
if (p_dev == NULL) {
return;
}
btc_hh_device_drop_cnt_inc(p_dev);
}
void btc_hh_reset_drop_pkt_cnt(uint8_t dev_handle)
{
btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_handle(dev_handle);
if (p_dev == NULL) {
return;
}
btc_hh_device_drop_cnt_set(p_dev, 0);
}
/*******************************************************************************
*
* Function btc_hh_find_dev_by_bda
@@ -337,9 +421,10 @@ void btc_hh_remove_device(BD_ADDR bd_addr)
* with up-layer */ //[boblane]
// HAL_CBACK(bt_hh_callbacks, connection_state_cb, &(p_dev->bd_addr), BTHH_CONN_STATE_DISCONNECTED);
btc_hh_device_drop_cnt_set(p_dev, 0);
p_dev->dev_status = ESP_HIDH_CONN_STATE_UNKNOWN;
p_dev->dev_handle = BTA_HH_INVALID_HANDLE;
p_dev->ready_for_data = false;
btc_hh_data_path_deinit_inner(p_dev);
if (btc_hh_cb.device_num > 0) {
btc_hh_cb.device_num--;
@@ -367,23 +452,6 @@ static void bte_hh_arg_deep_copy(btc_msg_t *msg, void *p_dst, void *p_src)
}
break;
}
case BTA_HH_DATA_IND_EVT: {
BT_HDR *src_hdr = p_src_data->int_data.p_data;
p_dst_data->int_data.p_data = NULL;
if (src_hdr) {
p_dst_data->int_data.p_data = osi_malloc(sizeof(BT_HDR) + src_hdr->len);
if (p_dst_data->int_data.p_data == NULL) {
BTC_TRACE_ERROR("%s malloc int_data.p_data failed!", __func__);
p_dst_data->int_data.status = ESP_HIDH_ERR_NO_RES;
break;
}
BT_HDR *dst_hdr = p_dst_data->int_data.p_data;
memcpy(dst_hdr, src_hdr, sizeof(BT_HDR));
memcpy(dst_hdr->data, src_hdr->data + src_hdr->offset, src_hdr->len);
dst_hdr->offset = 0;
}
break;
}
default:
break;
}
@@ -442,9 +510,6 @@ static void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH *p_data)
case BTA_HH_DATA_EVT:
param_len = sizeof(tBTA_HH_API_SENDDATA);
break;
case BTA_HH_DATA_IND_EVT:
param_len = sizeof(tBTA_HH_INTDATA);
break;
case BTA_HH_API_ERR_EVT:
param_len = 0;
break;
@@ -472,6 +537,7 @@ static void btc_hh_init(void)
{
BTC_TRACE_API("%s", __func__);
esp_hidh_status_t ret = ESP_HIDH_OK;
uint8_t i;
do {
if (is_hidh_init()) {
BTC_TRACE_ERROR("%s HH has been initiated, shall uninit first!", __func__);
@@ -480,8 +546,19 @@ static void btc_hh_init(void)
}
memset(&btc_hh_cb, 0, sizeof(btc_hh_cb));
for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) {
for (i = 0; i < BTC_HH_MAX_HID; i++) {
btc_hh_cb.devices[i].dev_status = ESP_HIDH_CONN_STATE_UNKNOWN;
if (!btc_hh_device_drop_cnt_init(&btc_hh_cb.devices[i])) {
BTC_TRACE_ERROR("%s: device lock init failed", __func__);
ret = ESP_HIDH_ERR_NO_RES;
break;
}
}
if (ret != ESP_HIDH_OK) {
while (i-- > 0) {
btc_hh_device_drop_cnt_deinit(&btc_hh_cb.devices[i]);
}
break;
}
BTA_HhEnable(BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT, bte_hh_evt);
} while (0);
@@ -1220,14 +1297,263 @@ static void btc_hh_cb_arg_deep_free(btc_msg_t *msg)
case BTA_HH_GET_RPT_EVT:
utl_freebuf((void **)&arg->hs_data.rsp_data.p_rpt_data);
break;
case BTA_HH_DATA_IND_EVT:
utl_freebuf((void **)&arg->int_data.p_data);
break;
default:
break;
}
}
static void btc_hh_data_path_deinit_inner(btc_hh_device_t *p_dev)
{
struct pkt_queue *queue;
struct osi_event *event;
if (p_dev == NULL) {
return;
}
osi_mutex_lock(&p_dev->lock, OSI_MUTEX_MAX_TIMEOUT);
queue = p_dev->data_queue;
event = p_dev->data_ready;
p_dev->data_queue = NULL;
p_dev->data_ready = NULL;
osi_mutex_unlock(&p_dev->lock);
if (event != NULL) {
osi_event_delete(event);
}
if (queue != NULL) {
pkt_queue_destroy(queue, bta_hh_co_data_linked_pkt_free);
}
}
bool btc_hh_data_enqueue_linked_pkt(pkt_linked_item_t *linked_pkt)
{
tBTA_HH_DATA_PKT *pkt = linked_pkt != NULL ? (tBTA_HH_DATA_PKT *)linked_pkt->data : NULL;
btc_hh_device_t *p_dev = pkt != NULL ? btc_hh_find_connected_dev_by_handle(pkt->dev_handle) : NULL;
struct pkt_queue *data_queue;
struct osi_event *data_ready;
pkt_linked_item_t *old = NULL;
bool enqueue_ok;
if (p_dev == NULL || pkt == NULL || pkt->p_buf == NULL || pkt->len == 0) {
return false;
}
osi_mutex_lock(&p_dev->lock, OSI_MUTEX_MAX_TIMEOUT);
data_queue = p_dev->data_queue;
data_ready = p_dev->data_ready;
if (data_queue == NULL) {
osi_mutex_unlock(&p_dev->lock);
return false;
}
if (pkt_queue_length(data_queue) >= BTC_HH_DATA_QUEUE_LEN_MAX) {
old = pkt_queue_dequeue(data_queue);
if (old != NULL) {
p_dev->drop_pkt_cnt++;
}
}
enqueue_ok = pkt_queue_enqueue(data_queue, linked_pkt);
if (enqueue_ok && data_ready != NULL) {
osi_thread_post_event(data_ready, 0);
}
osi_mutex_unlock(&p_dev->lock);
if (old != NULL) {
bta_hh_co_data_linked_pkt_free(old);
}
return enqueue_ok;
}
pkt_linked_item_t *btc_hh_data_dequeue_reusable_linked_pkt(uint8_t dev_handle)
{
btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_handle(dev_handle);
struct pkt_queue *data_queue;
pkt_linked_item_t *old;
if (p_dev == NULL) {
return NULL;
}
osi_mutex_lock(&p_dev->lock, OSI_MUTEX_MAX_TIMEOUT);
data_queue = p_dev->data_queue;
if (data_queue == NULL || pkt_queue_length(data_queue) < BTC_HH_DATA_QUEUE_LEN_MAX) {
osi_mutex_unlock(&p_dev->lock);
return NULL;
}
old = pkt_queue_dequeue(data_queue);
if (old != NULL) {
p_dev->drop_pkt_cnt++;
}
osi_mutex_unlock(&p_dev->lock);
return old;
}
bool btc_hh_data_enqueue_pkt(const tBTA_HH_DATA_PKT *pkt_meta)
{
pkt_linked_item_t *linked_pkt;
tBTA_HH_DATA_PKT *pkt;
if (pkt_meta == NULL || pkt_meta->p_buf == NULL || pkt_meta->len == 0) {
return false;
}
linked_pkt = (pkt_linked_item_t *)osi_malloc(BT_PKT_LINKED_HDR_SIZE + sizeof(tBTA_HH_DATA_PKT));
if (linked_pkt == NULL) {
return false;
}
pkt = (tBTA_HH_DATA_PKT *)linked_pkt->data;
*pkt = *pkt_meta;
if (!btc_hh_data_enqueue_linked_pkt(linked_pkt)) {
osi_free(linked_pkt);
return false;
}
return true;
}
static bool btc_hh_data_path_init_inner(btc_hh_device_t *p_dev)
{
bool result = false;
struct pkt_queue *data_queue = NULL;
struct osi_event *data_ready = NULL;
do {
if (p_dev == NULL) {
break;
}
osi_mutex_lock(&p_dev->lock, OSI_MUTEX_MAX_TIMEOUT);
if (p_dev->data_queue != NULL && p_dev->data_ready != NULL) {
osi_mutex_unlock(&p_dev->lock);
result = true;
break;
}
osi_mutex_unlock(&p_dev->lock);
btc_hh_data_path_deinit_inner(p_dev);
data_queue = pkt_queue_create();
if (data_queue == NULL) {
BTC_TRACE_ERROR("%s: pkt_queue_create failed", __func__);
break;
}
data_ready = osi_event_create(btc_hh_data_pkt_handler, p_dev);
if (data_ready == NULL) {
BTC_TRACE_ERROR("%s: osi_event_create failed", __func__);
break;
}
if (!osi_event_bind(data_ready, btc_get_current_thread(), BTC_HH_DATA_QUEUE_IDX)) {
BTC_TRACE_ERROR("%s: osi_event_bind failed", __func__);
break;
}
osi_mutex_lock(&p_dev->lock, OSI_MUTEX_MAX_TIMEOUT);
p_dev->data_queue = data_queue;
p_dev->data_ready = data_ready;
p_dev->drop_pkt_cnt = 0;
osi_mutex_unlock(&p_dev->lock);
data_queue = NULL;
data_ready = NULL;
result = true;
} while (0);
if (!result) {
btc_hh_data_path_deinit_inner(p_dev);
if (data_ready != NULL) {
osi_event_delete(data_ready);
}
if (data_queue != NULL) {
pkt_queue_destroy(data_queue, bta_hh_co_data_linked_pkt_free);
}
}
return result;
}
bool btc_hh_data_path_init(uint8_t dev_handle)
{
btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_handle(dev_handle);
return btc_hh_data_path_init_inner(p_dev);
}
static size_t btc_hh_get_batch_size(size_t queue_len)
{
size_t batch_size = BTC_HH_DATA_BATCH_SIZE_BASE;
if (queue_len >= BTC_HH_DATA_BATCH_SIZE_MAX) {
batch_size = BTC_HH_DATA_BATCH_SIZE_MAX;
} else if (queue_len >= BTC_HH_DATA_BATCH_SIZE_HIGH) {
batch_size = BTC_HH_DATA_BATCH_SIZE_HIGH;
}
return batch_size;
}
static void btc_hh_data_pkt_handler(void *arg)
{
tBTA_HH_DATA_PKT *pkt;
pkt_linked_item_t *linked_pkt;
btc_hh_device_t *p_dev = (btc_hh_device_t *)arg;
size_t pkts_to_process;
size_t batch_limit;
uint32_t drop_pkt_cnt;
if (p_dev == NULL || p_dev->data_queue == NULL) {
return;
}
drop_pkt_cnt = p_dev->drop_pkt_cnt;
if (drop_pkt_cnt) {
osi_mutex_lock(&p_dev->lock, OSI_MUTEX_MAX_TIMEOUT);
drop_pkt_cnt = p_dev->drop_pkt_cnt;
p_dev->drop_pkt_cnt = 0;
osi_mutex_unlock(&p_dev->lock);
BTC_TRACE_WARNING("hh hdl:%d drop %u pkts", p_dev->dev_handle, (unsigned int)drop_pkt_cnt);
}
pkts_to_process = pkt_queue_length(p_dev->data_queue);
batch_limit = btc_hh_get_batch_size(pkts_to_process);
if (pkts_to_process > batch_limit) {
pkts_to_process = batch_limit;
}
for (size_t i = 0; i < pkts_to_process; i++) {
esp_hidh_cb_param_t param = {0};
linked_pkt = pkt_queue_dequeue(p_dev->data_queue);
if (linked_pkt == NULL) {
break;
}
pkt = (tBTA_HH_DATA_PKT *)linked_pkt->data;
BTC_TRACE_DEBUG("handle = %d", pkt->dev_handle);
if (pkt->len > 0) {
param.data_ind.len = pkt->len;
param.data_ind.data = bta_hh_co_data_pkt_get_payload(pkt);
}
param.data_ind.handle = pkt->dev_handle;
param.data_ind.status = ESP_HIDH_OK;
param.data_ind.proto_mode = proto_mode_change_to_upper_layer(pkt->proto_mode);
btc_hh_cb_to_app(ESP_HIDH_DATA_IND_EVT, &param);
bta_hh_co_data_linked_pkt_free(linked_pkt);
}
if (p_dev->data_ready != NULL && !pkt_queue_is_empty(p_dev->data_queue)) {
osi_thread_post_event(p_dev->data_ready, 0);
}
}
bool btc_hh_copy_hid_info(tBTA_HH_DEV_DSCP_INFO *dest, tBTA_HH_DEV_DSCP_INFO *src)
{
dest->descriptor.dl_len = 0;
@@ -1287,12 +1613,20 @@ void btc_hh_cb_handler(btc_msg_t *msg)
btc_hh_cb.status = BTC_HH_ENABLED;
BTC_TRACE_DEBUG("Loading added devices");
/* Add hid descriptors for already bonded hid devices*/
// btc_storage_load_bonded_hid_info();
btc_storage_load_bonded_hid_info();
param.init.status = ESP_HIDH_OK;
} else {
for (i = 0; i < BTC_HH_MAX_HID; i++) {
btc_hh_device_drop_cnt_deinit(&btc_hh_cb.devices[i]);
}
memset(&btc_hh_cb, 0, sizeof(btc_hh_cb));
for (i = 0; i < BTC_HH_MAX_HID; i++) {
btc_hh_cb.devices[i].dev_status = ESP_HIDH_CONN_STATE_UNKNOWN;
}
btc_hh_cb.status = BTC_HH_DISABLED;
BTC_TRACE_ERROR("Error, HH enabling failed, status = %d", p_data->status);
param.init.status = p_data->status;
}
param.init.status = p_data->status;
btc_hh_cb_to_app(ESP_HIDH_INIT_EVT, &param);
break;
case BTA_HH_DISABLE_EVT:
@@ -1302,9 +1636,12 @@ void btc_hh_cb_handler(btc_msg_t *msg)
// btif_hd_service_registration();
btc_hh_cb.service_dereg_active = FALSE;
}
if (p_data->status == BTA_HH_OK) {
// Clear the control block
for (i = 0; i < BTC_HH_MAX_HID; i++) {
btc_hh_data_path_deinit_inner(&btc_hh_cb.devices[i]);
btc_hh_device_drop_cnt_deinit(&btc_hh_cb.devices[i]);
if (btc_hh_cb.devices[i].vup_timer) {
osi_alarm_free(btc_hh_cb.devices[i].vup_timer);
}
@@ -1343,9 +1680,8 @@ void btc_hh_cb_handler(btc_msg_t *msg)
// if (check_cod(&p_data->conn.bda, COD_HID_KEYBOARD) || check_cod(&p_data->conn.bda, COD_HID_COMBO))
// BTA_HhSetIdle(p_data->conn.handle, 0);
btc_hh_cb.p_curr_dev = btc_hh_find_connected_dev_by_handle(p_data->conn.handle);
BTA_HhGetDscpInfo(p_data->conn.handle);
p_dev->dev_status = ESP_HIDH_CONN_STATE_CONNECTED;
BTA_HhGetDscpInfo(p_data->conn.handle);
param.open.status = ESP_HIDH_OK;
param.open.conn_status = ESP_HIDH_CONN_STATE_CONNECTED;
}
@@ -1354,6 +1690,7 @@ void btc_hh_cb_handler(btc_msg_t *msg)
if (p_dev != NULL) {
btc_hh_stop_vup_timer(p_dev->bd_addr);
p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED;
btc_hh_data_path_deinit_inner(p_dev);
}
btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED;
@@ -1419,10 +1756,13 @@ void btc_hh_cb_handler(btc_msg_t *msg)
case BTA_HH_CLOSE_EVT:
BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status,
p_data->dev_status.handle);
btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED;
p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle);
if (p_dev != NULL) {
BTC_TRACE_DEBUG("uhid local_vup=%d", p_dev->local_vup);
btc_hh_stop_vup_timer(p_dev->bd_addr);
p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED;
btc_hh_data_path_deinit_inner(p_dev);
/* If this is a locally initiated VUP, remove the bond as ACL got
* disconnected while VUP being processed.
*/
@@ -1432,11 +1772,7 @@ void btc_hh_cb_handler(btc_msg_t *msg)
BTA_DmRemoveDevice(p_dev->bd_addr, BT_TRANSPORT_BR_EDR);
#endif
btc_hh_remove_device(p_dev->bd_addr);
} else {
p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED;
}
btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED;
param.close.status = p_data->dev_status.status;
} else {
BTC_TRACE_ERROR("Error: cannot find device with handle %d", p_data->dev_status.handle);
@@ -1449,12 +1785,13 @@ void btc_hh_cb_handler(btc_msg_t *msg)
case BTA_HH_VC_UNPLUG_EVT:
BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status,
p_data->dev_status.handle);
p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle);
btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED;
p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle);
if (p_dev != NULL) {
/* Stop the VUP timer */
btc_hh_stop_vup_timer(p_dev->bd_addr);
p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED;
btc_hh_data_path_deinit_inner(p_dev);
BTC_TRACE_DEBUG("%s---Sending connection state change", __func__);
param.close.status = ESP_HIDH_OK;
param.close.handle = p_data->dev_status.handle;
@@ -1577,17 +1914,6 @@ void btc_hh_cb_handler(btc_msg_t *msg)
memcpy(param.rmv_dev.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN);
btc_hh_cb_to_app(ESP_HIDH_RMV_DEV_EVT, &param);
break;
case BTA_HH_DATA_IND_EVT:
BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->int_data.status, p_data->int_data.handle);
if (p_data->int_data.status == BTA_HH_OK && p_data->int_data.p_data) {
param.data_ind.len = p_data->int_data.p_data->len;
param.data_ind.data = p_data->int_data.p_data->data + p_data->int_data.p_data->offset;
}
param.data_ind.handle = p_data->int_data.handle;
param.data_ind.status = p_data->int_data.status;
param.data_ind.proto_mode = proto_mode_change_to_upper_layer(p_data->int_data.proto_mode);
btc_hh_cb_to_app(ESP_HIDH_DATA_IND_EVT, &param);
break;
case BTA_HH_API_ERR_EVT:
break;
default:

View File

@@ -22,11 +22,18 @@
#include <stdint.h>
#include "bta/bta_hh_api.h"
#include "bta/bta_hh_co.h"
#include "stack/bt_types.h"
#include "btc/btc_task.h"
#include "osi/alarm.h"
#include "osi/pkt_queue.h"
#include "osi/thread.h"
#include "osi/mutex.h"
#include "esp_hidh_api.h"
#define BTC_HH_MAX_HID 8
#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE)
#define BTC_HH_MAX_HID BTA_HH_MAX_DEVICE
#define BTC_HH_MAX_ADDED_DEV 32
#define BTC_HH_MAX_KEYSTATES 3
@@ -67,15 +74,18 @@ typedef enum {
} BTC_HH_STATUS;
typedef struct {
esp_hidh_connection_state_t dev_status;
uint8_t dev_status; // see esp_hidh_connection_state_t
uint8_t dev_handle;
BD_ADDR bd_addr;
uint16_t attr_mask;
uint8_t sub_class;
uint8_t app_id;
bool ready_for_data;
osi_alarm_t *vup_timer;
bool local_vup; // Indicated locally initiated VUP
BD_ADDR bd_addr;
uint16_t attr_mask;
uint32_t drop_pkt_cnt;
osi_mutex_t lock;
struct pkt_queue *data_queue;
struct osi_event *data_ready;
osi_alarm_t *vup_timer;
} btc_hh_device_t;
/* Control block to maintain properties of devices */
@@ -187,4 +197,13 @@ bool btc_hh_add_added_dev(BD_ADDR bd_addr, uint16_t attr_mask);
void btc_hh_get_profile_status(esp_hidh_profile_status_t *param);
btc_hh_device_t *btc_hh_find_connected_dev_by_handle(uint8_t handle);
bool btc_hh_data_enqueue_pkt(const tBTA_HH_DATA_PKT *pkt_meta);
bool btc_hh_data_enqueue_linked_pkt(pkt_linked_item_t *linked_pkt);
pkt_linked_item_t *btc_hh_data_dequeue_reusable_linked_pkt(uint8_t dev_handle);
void btc_hh_on_pkt_dropped(uint8_t dev_handle);
void btc_hh_reset_drop_pkt_cnt(uint8_t dev_handle);
bool btc_hh_data_path_init(uint8_t dev_handle);
#endif /* (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) */
#endif /* BTC_HH_H */