From 87dd2dc16abd2c7f3fc676957a7759c9c80a731e Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 21 Apr 2018 08:38:08 -0600 Subject: [PATCH] wireless/bluetooth: Adds implementation of GATT read IOCTL commands. --- include/nuttx/wireless/bt_ioctl.h | 13 +- wireless/bluetooth/bt_gatt.c | 2 +- wireless/bluetooth/bt_ioctl.c | 232 ++++++++++++++++++++++++++++-- 3 files changed, 230 insertions(+), 17 deletions(-) diff --git a/include/nuttx/wireless/bt_ioctl.h b/include/nuttx/wireless/bt_ioctl.h index 67d4777468..fbe52682b9 100644 --- a/include/nuttx/wireless/bt_ioctl.h +++ b/include/nuttx/wireless/bt_ioctl.h @@ -55,9 +55,12 @@ * Pre-processor Definitions ****************************************************************************/ +/* Size limits of things for fixed allocations (most are arbitrary) */ + #define HCI_DEVNAME_SIZE 32 /* Maximum size of node name */ #define HCI_FEATURES_SIZE 8 /* LMP features */ -#define HCI_GATT_MAXHANDLES 9 /* Max handles in GATT read multiple IOCTL */ +#define HCI_GATT_MAXHANDLES 8 /* Max handles in GATT read multiple IOCTL */ +#define HCI_GATTRD_DATA 32 /* Max number of bytes in GATT read data */ /* Bluetooth network device IOCTL commands. */ @@ -242,11 +245,12 @@ #define btr_grsp btru.btrdg.btrdg_grsp #define btr_rdpeer btru.btgrd.btgrd_rdpeer -#define btr_rdnhandle btru.btgrd.btgrd_rdnhandles +#define btr_rdoffset btru.btgrd.btgrd_rdoffset +#define btr_rdnhandles btru.btgrd.btgrd_rdnhandles #define btr_rdhandles btru.btgrd.btgrd_rdhandles #define btr_rdpending btru.btgrr.btgrr_rdpending -#define btr_rdesult btru.btgrr.btgrr_rdesult +#define btr_rdresult btru.btgrr.btgrr_rdresult #define btr_rdsize btru.btgrr.btgrr_rdsize #define btr_rddata btru.btgrr.btgrr_rddata @@ -456,6 +460,7 @@ struct btreq_s { bt_addr_le_t btgrd_rdpeer; /* Peer address */ uint8_t btgrd_rdnhandles; /* Number of handles in array */ + uint16_t btgrd_rdoffset; /* Offset (Only for read single) */ uint16_t btgrd_rdhandles[HCI_GATT_MAXHANDLES]; } btgrd; @@ -464,7 +469,7 @@ struct btreq_s struct { bool btgrr_rdpending; /* True: Read not yet complete */ - uint8_t btgrr_rdesult; /* Result of the read */ + uint8_t btgrr_rdresult; /* Result of the read */ uint8_t btgrr_rdsize; /* Input: Sizeof rddata[] * Output: Number of valid bytes */ FAR uint8_t *btgrr_rddata; /* Values returned by read */ diff --git a/wireless/bluetooth/bt_gatt.c b/wireless/bluetooth/bt_gatt.c index 3437b89869..830a0da1ad 100644 --- a/wireless/bluetooth/bt_gatt.c +++ b/wireless/bluetooth/bt_gatt.c @@ -1208,7 +1208,7 @@ void bt_gatt_cancel(FAR struct bt_conn_s *conn) } int bt_gatt_read_multiple(FAR struct bt_conn_s *conn, - FAR const uint16_t * handles, size_t count, + FAR const uint16_t *handles, size_t count, bt_gatt_read_func_t func) { FAR struct bt_buf_s *buf; diff --git a/wireless/bluetooth/bt_ioctl.c b/wireless/bluetooth/bt_ioctl.c index 2ba4c0d918..e2e0c07a4d 100644 --- a/wireless/bluetooth/bt_ioctl.c +++ b/wireless/bluetooth/bt_ioctl.c @@ -91,6 +91,17 @@ struct btnet_discoverstate_s struct bt_discresonse_s bd_rsp[CONFIG_BLUETOOTH_MAXDISCOVER]; }; +/* GATT read state variables. */ + +struct btnet_rdstate_s +{ + bool rd_pending; /* True: result not yet received */ + uint8_t rd_result; /* The result of the read */ + uint8_t rd_head; /* Start of data to read */ + uint8_t rd_tail; /* End data to read */ + uint8_t rd_data[HCI_GATTRD_DATA]; /* Data read */ +}; + /**************************************************************************** * Private Data ****************************************************************************/ @@ -100,11 +111,20 @@ struct btnet_discoverstate_s * * NOTE: This limits to a single Bluetooth device with one concurrent scan * action, one concurrent MTU exchange, and one concurrent discovery action. + * + * REVISIT: A fix might be to (1) allocate instances on each IOCTL command + * the starts an operation, keeping the allocated structures in a list. (2) + * Return a reference number with each such command. That reference number + * would then be used in each IOCTL command that gets the result of the + * previously requested data. (3) The allocated instance would be freed + * wither (1) it is empty or (2) it has expired without being harvested. */ static struct btnet_scanstate_s g_scanstate; static struct btnet_discoverstate_s g_discoverstate; static struct bt_result_s g_exchangeresult; +static struct btnet_rdstate_s g_rdstate; +static struct bt_result_s g_gattwrresult; /**************************************************************************** * Private Functions @@ -277,7 +297,7 @@ static int btnet_scan_result(FAR struct bt_scanresponse_s *result, } /**************************************************************************** - * Name: bt_exchange_rsp + * Name: btnet_exchange_rsp * * Description: * Result of MTU exchange. @@ -291,15 +311,15 @@ static int btnet_scan_result(FAR struct bt_scanresponse_s *result, * ****************************************************************************/ -static void bt_exchange_rsp(FAR struct bt_conn_s *conn, uint8_t result) +static void btnet_exchange_rsp(FAR struct bt_conn_s *conn, uint8_t result) { wlinfo("Exchange %s\n", result == 0 ? "succeeded" : "failed"); - g_exchangeresult.br_pending = true; + g_exchangeresult.br_pending = false; g_exchangeresult.br_result = result; } /**************************************************************************** - * Name: bt_discover_func + * Name: btnet_discover_func * * Description: * GATT discovery callback. This function is called when a new handle is @@ -314,8 +334,8 @@ static void bt_exchange_rsp(FAR struct bt_conn_s *conn, uint8_t result) * ****************************************************************************/ -static uint8_t bt_discover_func(FAR const struct bt_gatt_attr_s *attr, - FAR void *arg) +static uint8_t btnet_discover_func(FAR const struct bt_gatt_attr_s *attr, + FAR void *arg) { uint8_t nexttail; uint8_t head; @@ -377,7 +397,7 @@ static uint8_t bt_discover_func(FAR const struct bt_gatt_attr_s *attr, } /**************************************************************************** - * Name: bt_discover_destroy + * Name: btnet_discover_destroy * * Description: * GATT destroy callback. This function is called when a discovery @@ -391,7 +411,7 @@ static uint8_t bt_discover_func(FAR const struct bt_gatt_attr_s *attr, * ****************************************************************************/ -static void bt_discover_destroy(FAR void *arg) +static void btnet_discover_destroy(FAR void *arg) { FAR struct bt_gatt_discover_params *params = arg; @@ -481,6 +501,98 @@ static int btnet_discover_result(FAR struct bt_discresonse_s *result, return nrsp; } +/**************************************************************************** + * Name: btnet_read_callback + * + * Description: + * Handle network IOCTL commands directed to this device. + * + * Input Parameters: + * conn - Connection of read + * result - The result status of the read. + * data - The data read + * length - The Number of bytes read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void btnet_read_callback(FAR struct bt_conn_s *conn, int result, + FAR const void *data, uint16_t length) +{ + wlinfo("Read complete: result %d length %u\n", result, length); + + DEBUGASSERT(conn != NULL && g_rdstate.rd_pending); + + if (length > HCI_GATTRD_DATA) + { + g_rdstate.rd_result = ENFILE; + } + else + { + DEBUGASSERT((unsigned int)result < UINT8_MAX); + + g_rdstate.rd_result = result; + g_rdstate.rd_head = 0; + g_rdstate.rd_tail = length; + memcpy(g_rdstate.rd_data, data, length); + } + + g_rdstate.rd_pending = false; +} + +/**************************************************************************** + * Name: btnet_read_result + * + * Description: + * Handle network IOCTL commands directed to this device. + * + * Input Parameters: + * btreq - IOCTL command data + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +static int btnet_read_result(FAR struct btreq_s *btreq) +{ + int head; + int rdlen; + + DEBUGASSERT(btreq != NULL && btreq->btr_rddata != NULL); + + /* Is the read complete? */ + + btreq->btr_rdpending = g_rdstate.rd_pending; + if (!g_rdstate.rd_pending) + { + return -EBUSY; + } + + /* Yes... return the read data */ + + head = g_rdstate.rd_head; + if (head >= g_rdstate.rd_tail) + { + return -ENODATA; + } + + rdlen = btreq->btr_rdsize; + if (rdlen + head >= g_rdstate.rd_tail) + { + rdlen = g_rdstate.rd_tail - head; + } + + btreq->btr_rdresult = g_rdstate.rd_result; + btreq->btr_rdsize = rdlen; + memcpy(btreq->btr_rddata, &g_rdstate.rd_data[head], rdlen); + + g_rdstate.rd_head = head + rdlen; + return OK; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -711,7 +823,7 @@ int btnet_ioctl(FAR struct net_driver_s *netdev, int cmd, unsigned long arg) } else { - ret = bt_gatt_exchange_mtu(conn, bt_exchange_rsp); + ret = bt_gatt_exchange_mtu(conn, btnet_exchange_rsp); if (ret == OK) { g_exchangeresult.br_pending = true; @@ -768,8 +880,8 @@ int btnet_ioctl(FAR struct net_driver_s *netdev, int cmd, unsigned long arg) params = &g_discoverstate.bd_params; params->uuid = &g_discoverstate.bd_uuid; - params->func = bt_discover_func; - params->destroy = bt_discover_destroy; + params->func = btnet_discover_func; + params->destroy = btnet_discover_destroy; params->start_handle = btreq->btr_dstart; params->end_handle = btreq->btr_dend; @@ -803,7 +915,7 @@ int btnet_ioctl(FAR struct net_driver_s *netdev, int cmd, unsigned long arg) if (ret < 0) { wlerr("ERROR: Failed to start discovery: %d\n", ret); - bt_discover_destroy(params); + btnet_discover_destroy(params); } bt_conn_release(conn); @@ -829,6 +941,102 @@ int btnet_ioctl(FAR struct net_driver_s *netdev, int cmd, unsigned long arg) } break; + /* SIOCBTGATTRD: Initiate read of GATT data */ + + case SIOCBTGATTRD: + { + /* Is there already a read in progress? The current + * implementation can support only one read at a time. + * REVISIT.. see suggested design improvement above. + */ + + if (g_rdstate.rd_pending) + { + wlwarn("WARNING: Read pending\n"); + ret = -EBUSY; + } + else + { + FAR struct bt_conn_s *conn; + + /* Get the connection associated with the provided LE address */ + + conn = bt_conn_lookup_addr_le(&btreq->btr_rdpeer); + if (conn == NULL) + { + wlwarn("WARNING: Peer not connected\n"); + ret = -ENOTCONN; + } + else + { + /* Set up for the read */ + + g_rdstate.rd_pending = true; + g_rdstate.rd_head = 0; + g_rdstate.rd_tail = 0; + + /* Initiate read.. single or multiple? */ + + if (btreq->btr_rdnhandles == 1) + { + /* Read single */ + + ret = bt_gatt_read(conn, btreq->btr_rdhandles[0], + btreq->btr_rdoffset, + btnet_read_callback); + } + else if (btreq->btr_rdnhandles < HCI_GATT_MAXHANDLES) + { + /* Read multiple */ + + DEBUGASSERT(btreq->btr_rdnhandles > 0); + ret = bt_gatt_read_multiple(conn, btreq->btr_rdhandles, + btreq->btr_rdnhandles, + btnet_read_callback); + } + else + { + ret = -ENFILE; + } + + if (ret < 0) + { + wlerr("ERROR: Read operation failed: %d\n", ret); + } + + bt_conn_release(conn); + } + } + } + break; + + /* SIOCBTGATTRDGET: Get the result of the GATT data read */ + + case SIOCBTGATTRDGET: + { + ret = btnet_read_result(btreq); + wlinfo("Get read results: %d\n", ret); + } + break; + + /* SIOCBTGATTWR: Write GATT data */ + + case SIOCBTGATTWR: + { + ret = -ENOSYS; + } + break; + + /* SIOCBTGATTWRGET: Get the result of the GATT data write */ + + case SIOCBTGATTWRGET: + { + btreq->btr_wrpending = g_gattwrresult.br_pending; + btreq->btr_wrresult = g_gattwrresult.br_result; + ret = OK; + } + break; + default: wlwarn("WARNING: Unrecognized IOCTL command: %02x\n", cmd); ret = -ENOTTY;