From accef0ca9e96ced73ebe3e141975993edb52d42e Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 29 Mar 2018 12:35:41 -0600 Subject: [PATCH] This commit brings in the Bluetooth stack from the Intel/Zephyr arduino101_firmware_source-v1.tar package. NOTE: This selection is marked EXPERIMENTAL. It is incomplete and, hence, untested. It still lacks any low-level Bluetooth drivers and is missing the network interface driver. Squashed commit of the following: wireless/bluetooth: Fixe last of compile issues. Now compiles without errors or warnings. wireless/blutooth: Add macros BT_GETUINT16() and BT_PUTUINT16(). Fix more compile errors. Only one file now generates compile errors. wireless/bluetooth: Add macros BT_LE162HOST() and BT_HOST2LE16(). wireless/bluetooth: Add bt_queue.c; begin fixing comple errors. Many more compile problems yet to resolve. Kconfig edited online with Bitbucket wireless/bluetooth: Struggling to remove nano_fifo logic: Replace buffer management with IOB allocate... this changes some logic and might have some side effects. Use messages queues instead of nano-fifos to inter-task communications. nano-fifos still used in 'frag' logic... whatever that is. wireless/bluetooth: Fix numerous typos introduced by an ill conceived search-and-replace. wireless/bluetooth: Add message queue support to manage interthread buffer transfers. wireless/bluetooth: Replace fibers with kernel threads. wireless/bluetooth: Fix a few initial compile errors. Just the tip of the iceberg. wireless/bluetooth: Complete leveage of the bluetooth stack including public header files. wireless/bluetooth: Complete leverage of all Bluetooth source files. Still missing header files that defines the driver interface. Also missing the network driver implementation. wireless/bluetooth: Fix some naming of static global variables. wireless/bluetooth: Adds three more files ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package (plus two original files). wireless/bluetooth: Adds five more files ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package. wireless/bluetooth: Adds three more files ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package. wireless/bluetooth: First few files ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package. --- include/nuttx/wireless/bt_buf.h | 327 +++++ include/nuttx/wireless/bt_conn.h | 232 ++++ include/nuttx/wireless/bt_core.h | 327 +++++ include/nuttx/wireless/bt_driver.h | 86 ++ include/nuttx/wireless/bt_gatt.h | 1048 +++++++++++++++ include/nuttx/wireless/bt_hci.h | 467 +++++++ include/nuttx/wireless/bt_uuid.h | 220 ++++ wireless/Kconfig | 1 + wireless/Makefile | 1 + wireless/bluetooth/Kconfig | 125 ++ wireless/bluetooth/Make.defs | 47 + wireless/bluetooth/bt_atomic.c | 147 +++ wireless/bluetooth/bt_atomic.h | 101 ++ wireless/bluetooth/bt_att.c | 1907 ++++++++++++++++++++++++++++ wireless/bluetooth/bt_att.h | 451 +++++++ wireless/bluetooth/bt_buf.c | 221 ++++ wireless/bluetooth/bt_conn.c | 808 ++++++++++++ wireless/bluetooth/bt_conn.h | 156 +++ wireless/bluetooth/bt_gatt.c | 1213 ++++++++++++++++++ wireless/bluetooth/bt_hcicore.c | 1669 ++++++++++++++++++++++++ wireless/bluetooth/bt_hcicore.h | 223 ++++ wireless/bluetooth/bt_keys.c | 376 ++++++ wireless/bluetooth/bt_keys.h | 107 ++ wireless/bluetooth/bt_l2cap.c | 425 +++++++ wireless/bluetooth/bt_l2cap.h | 154 +++ wireless/bluetooth/bt_queue.c | 213 ++++ wireless/bluetooth/bt_queue.h | 147 +++ wireless/bluetooth/bt_smp.c | 1641 ++++++++++++++++++++++++ wireless/bluetooth/bt_smp.h | 179 +++ wireless/bluetooth/bt_uuid.c | 125 ++ 30 files changed, 13144 insertions(+) create mode 100644 include/nuttx/wireless/bt_buf.h create mode 100644 include/nuttx/wireless/bt_conn.h create mode 100644 include/nuttx/wireless/bt_core.h create mode 100644 include/nuttx/wireless/bt_driver.h create mode 100644 include/nuttx/wireless/bt_gatt.h create mode 100644 include/nuttx/wireless/bt_hci.h create mode 100644 include/nuttx/wireless/bt_uuid.h create mode 100644 wireless/bluetooth/Kconfig create mode 100644 wireless/bluetooth/Make.defs create mode 100644 wireless/bluetooth/bt_atomic.c create mode 100644 wireless/bluetooth/bt_atomic.h create mode 100644 wireless/bluetooth/bt_att.c create mode 100644 wireless/bluetooth/bt_att.h create mode 100644 wireless/bluetooth/bt_buf.c create mode 100644 wireless/bluetooth/bt_conn.c create mode 100644 wireless/bluetooth/bt_conn.h create mode 100644 wireless/bluetooth/bt_gatt.c create mode 100644 wireless/bluetooth/bt_hcicore.c create mode 100644 wireless/bluetooth/bt_hcicore.h create mode 100644 wireless/bluetooth/bt_keys.c create mode 100644 wireless/bluetooth/bt_keys.h create mode 100644 wireless/bluetooth/bt_l2cap.c create mode 100644 wireless/bluetooth/bt_l2cap.h create mode 100644 wireless/bluetooth/bt_queue.c create mode 100644 wireless/bluetooth/bt_queue.h create mode 100644 wireless/bluetooth/bt_smp.c create mode 100644 wireless/bluetooth/bt_smp.h create mode 100644 wireless/bluetooth/bt_uuid.c diff --git a/include/nuttx/wireless/bt_buf.h b/include/nuttx/wireless/bt_buf.h new file mode 100644 index 00000000000..cb3cc265893 --- /dev/null +++ b/include/nuttx/wireless/bt_buf.h @@ -0,0 +1,327 @@ +/**************************************************************************** + * wireless/bluetooth/bt_att.h + * Bluetooth buffer management. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_WIRELESS_BT_BUF_H +#define __INCLUDE_NUTTX_WIRELESS_BT_BUF_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* BT_BUF_MAX_DATA + * Maximum amount of data that can fit in a buffer. + * + * The biggest foreseeable buffer size requirement right now comes from + * the Bluetooth 4.2 SMP MTU which is 65. This then become 65 + 4 (L2CAP + * header) + 4 (ACL header) + 1 (H4 header) = 74. This also covers the + * biggest HCI commands and events which are a bit under the 70 byte + * mark. + */ + +#define BT_BUF_MAX_DATA 74 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Type of data contained in a buffer */ + +enum bt_buf_type_e +{ + BT_CMD, /* HCI command */ + BT_EVT, /* HCI event */ + BT_ACL_OUT, /* Outgoing ACL data */ + BT_ACL_IN, /* Incoming ACL data */ + BT_DUMMY = BT_CMD /* Only used for waking up kernel threads */ +}; + +/* HCI command specific information */ + +struct bt_buf_hci_data_s +{ + /* Used by bt_hci_cmd_send_sync. Initially contains the waiting + * semaphore, as the semaphore is given back contains the bt_buf + * for the return parameters. + */ + + FAR void *sync; + + /* The command opcode that the buffer contains */ + + uint16_t opcode; +}; + +/* ACL data buffer specific information */ + +struct bt_buf_acl_data_s +{ + uint16_t handle; +}; + +struct bt_buf_s +{ + FAR struct iob_s *iob; /* IOB container of the buffer */ + union + { + struct bt_buf_hci_data_s hci; + struct bt_buf_acl_data_s acl; + } u; + + FAR uint8_t *data; /* Start of data in the buffer */ + uint8_t len; /* Length of data in the buffer */ + uint8_t ref : 5; /* Reference count */ + uint8_t type : 3; /* Type of data contained in the buffer */ + + /* The full available buffer. */ + + uint8_t buf[BT_BUF_MAX_DATA]; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_buf_get + * + * Description: + * Get buffer from the available buffers pool with specified type and + * reserved headroom. + * + * Input Parameters: + * type - Buffer type. + * reserve_head - How much headroom to reserve. + * + * Returned Value: + * New buffer or NULL if out of buffers. + * + * WARNING: If there are no available buffers and the function is + * called from a task or thread the call will block until a buffer + * becomes available in the pool. + * + ****************************************************************************/ + +FAR struct bt_buf_s *bt_buf_get(enum bt_buf_type_e type, size_t reserve_head); + +/**************************************************************************** + * Name: bt_buf_put + * + * Description: + * Decrements the reference count of a buffer and puts it back into the + * pool if the count reaches zero. + * + * Input Parameters: + * buf - Buffer. + * + ****************************************************************************/ + +void bt_buf_put(FAR struct bt_buf_s *buf); + +/**************************************************************************** + * Name: bt_buf_hold + * + * Description: + * Increment the reference count of a buffer. + * + * Input Parameters: + * buf - Buffer. + * + ****************************************************************************/ + +FAR struct bt_buf_s *bt_buf_hold(FAR struct bt_buf_s *buf); + +/**************************************************************************** + * Name: bt_buf_add + * + * Description: + * Increments the data length of a buffer to account for more data + * at the end. + * + * Input Parameters: + * buf - Buffer to update. + * len - Number of bytes to increment the length with. + * + * Returned Value: + * The original tail of the buffer. + * + ****************************************************************************/ + +FAR void *bt_buf_add(FAR struct bt_buf_s *buf, size_t len); + +/**************************************************************************** + * Name: bt_buf_add_le16 + * + * Description: + * Adds 16-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * Input Parameters: + * buf - Buffer to update. + * value - 16-bit value to be added. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void bt_buf_add_le16(FAR struct bt_buf_s *buf, uint16_t value); + +/**************************************************************************** + * Name: bt_buf_push + * + * Description: + * Modifies the data pointer and buffer length to account for more data + * in the beginning of the buffer. + * + * Input Parameters: + * buf - Buffer to update. + * len - Number of bytes to add to the beginning. + * + * Returned Value: + * The new beginning of the buffer data. + * + ****************************************************************************/ + +FAR void *bt_buf_push(FAR struct bt_buf_s *buf, size_t len); + +/**************************************************************************** + * Name: bt_buf_pull + * + * Description: + * Removes data from the beginning of the buffer by modifying the data + * pointer and buffer length. + * + * Input Parameters: + * len - Number of bytes to remove. + * + * Returned Value: + * New beginning of the buffer data. + * + ****************************************************************************/ + +FAR void *bt_buf_pull(FAR struct bt_buf_s *buf, size_t len); + +/**************************************************************************** + * Name: bt_buf_pull_le16 + * + * Description: + * Same idea as with bt_buf_pull(), but a helper for operating on + * 16-bit little endian data. + * + * Input Parameters: + * buf - Buffer. + * + * Returned Value: + * 16-bit value converted from little endian to host endian. + * + ****************************************************************************/ + +uint16_t bt_buf_pull_le16(FAR struct bt_buf_s *buf); + +/**************************************************************************** + * Name: bt_buf_tailroom + * + * Description: + * Check how much free space there is at the end of the buffer. + * + * Returned Value: + * Number of bytes available at the end of the buffer. + * + ****************************************************************************/ + +size_t bt_buf_tailroom(FAR struct bt_buf_s *buf); + +/**************************************************************************** + * Name: bt_buf_headroom + * + * Description: + * Check how much free space there is in the beginning of the buffer. + * + * Returned Value: + * Number of bytes available in the beginning of the buffer. + * + ****************************************************************************/ + +size_t bt_buf_headroom(FAR struct bt_buf_s *buf); + +/**************************************************************************** + * Name: bt_buf_tail + * + * Description: + * Get a pointer to the end of the data in a buffer. + * + * Input Parameters: + * buf - Buffer. + * + * Returned Value: + * Tail pointer for the buffer. + * + ****************************************************************************/ + +#define bt_buf_tail(buf) ((buf)->data + (buf)->len) + +/**************************************************************************** + * Name: bt_buf_init + * + * Description: + * Initialize the buffers with specified amount of incoming and outgoing + * ACL buffers. The HCI command and event buffers will be allocated from + * whatever is left over. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero on success or (negative) error code on failure. + * + ****************************************************************************/ + +int bt_buf_init(void); + +#endif /* __INCLUDE_NUTTX_WIRELESS_BT_BUF_H */ diff --git a/include/nuttx/wireless/bt_conn.h b/include/nuttx/wireless/bt_conn.h new file mode 100644 index 00000000000..5088a732504 --- /dev/null +++ b/include/nuttx/wireless/bt_conn.h @@ -0,0 +1,232 @@ +/**************************************************************************** + * wireless/bluetooth/bt_conn.h + * Bluetooth connection handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_WIRELESS_BT_CONN_H +#define __INCLUDE_NUTTX_WIRELESS_BT_CONN_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Connection callback structure */ + +struct bt_conn_s; /* Forward Reference */ +struct bt_conn_cb_s +{ + CODE void (*connected)(FAR struct bt_conn_s *conn); + CODE void (*disconnected)(FAR struct bt_conn_s *conn); + FAR struct bt_conn_cb_s *next; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_conn_get + * + * Description: + * Increment the reference count of a connection object. + * + * Input Parameters: + * conn - Connection object. + * + * Returned Value: + * Connection object with incremented reference count. + * + ****************************************************************************/ + +FAR struct bt_conn_s *bt_conn_get(FAR struct bt_conn_s *conn); + +/**************************************************************************** + * Name: bt_conn_put + * + * Description: + * Decrement the reference count of a connection object. + * + * Input Parameters: + * conn - Connection object. + * + ****************************************************************************/ + +void bt_conn_put(FAR struct bt_conn_s *conn); + +/**************************************************************************** + * Name: bt_conn_lookup_addr_le + * + * Description: + * Look up an existing connection based on the remote address. + * + * Input Parameters: + * peer - Remote address. + * + * Returned Value: + * Connection object or NULL if not found. The caller gets a new reference + * to the connection object which must be released with bt_conn_put() once + * done using the object. + * + ****************************************************************************/ + +FAR struct bt_conn_s *bt_conn_lookup_addr_le(const bt_addr_le_t *peer); + +/**************************************************************************** + * Name: bt_conn_get_dst + * + * Description: + * Get destination (peer) address of a connection. + * + * Input Parameters: + * conn - Connection object. + * + * Returned Value: + * Destination address. + * + ****************************************************************************/ + +FAR const bt_addr_le_t *bt_conn_get_dst(FAR const struct bt_conn_s *conn); + +/**************************************************************************** + * Name: bt_conn_disconnect + * + * Description: + * Disconnect an active connection with the specified reason code or cancel + * pending outgoing connection. + * + * Input Parameters: + * conn - Connection to disconnect. + * reason - Reason code for the disconnection. + * + * Returned Value: + * Zero on success or (negative) error code on failure. + * + ****************************************************************************/ + +int bt_conn_disconnect(FAR struct bt_conn_s *conn, uint8_t reason); + +/**************************************************************************** + * Name: bt_conn_create_le + * + * Description: + * Allows initiate new LE link to remote peer using its address. + * Returns a new reference that the the caller is responsible for managing. + * + * Input Parameters: + * peer - Remote address. + * + * Returned Value: + * Valid connection object on success or NULL otherwise. + * + ****************************************************************************/ + +FAR struct bt_conn_s *bt_conn_create_le(const bt_addr_le_t *peer); + +/**************************************************************************** + * Name: bt_conn_security + * + * Description: + * This function enable security (encryption) for a connection. If device is + * already paired with sufficiently strong key encryption will be enabled. If + * link is already encrypted with sufficiently strong key this function does + * nothing. + * + * If device is not paired pairing will be initiated. If device is paired and + * keys are too weak but input output capabilities allow for strong enough keys + * pairing will be initiated. + * + * This function may return error if required level of security is not possible + * to achieve due to local or remote device limitation (eg input output + * capabilities). + * + * Input Parameters: + * conn - Connection object. + * sec - Requested security level. + * + * Returned Value: + * 0 on success or negative error + * + ****************************************************************************/ + +int bt_conn_security(FAR struct bt_conn_s *conn, enum bt_security_e sec); + +/**************************************************************************** + * Name: bt_conn_cb_register + * + * Description: + * Register callbacks to monitor the state of connections. + * + * Input Parameters: + * cb - Callback struct. + * + ****************************************************************************/ + +void bt_conn_cb_register(struct bt_conn_cb_s *cb); + +/**************************************************************************** + * Name: + * + * Description: + * This function enables/disables automatic connection initiation. + * Every time the device looses the connection with peer, this connection + * will be re-established if connectible advertisement from peer is + * received. + * + * Input Parameters: + * conn - Existing connection object. + * auto_conn - boolean value. If true, auto connect is enabled, if false, + * auto connect is disabled. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void bt_conn_set_auto_conn(FAR struct bt_conn_s *conn, bool auto_conn); + +#endif /* __INCLUDE_NUTTX_WIRELESS_BT_CONN_H */ diff --git a/include/nuttx/wireless/bt_core.h b/include/nuttx/wireless/bt_core.h new file mode 100644 index 00000000000..0c6dff1bcae --- /dev/null +++ b/include/nuttx/wireless/bt_core.h @@ -0,0 +1,327 @@ +/**************************************************************************** + * wireless/bluetooth/bt_core.h + * Bluetooth subsystem core APIs. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_WIRELESS_BT_CORE_H +#define __INCLUDE_NUTTX_WIRELESS_BT_CORE_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* BT_ADDR_STR_LEN + * Recommended length of user string buffer for Bluetooth address + * + * The recommended length guarantee the output of address conversion will not + * lose valuable information about address being processed. + */ + +#define BT_ADDR_STR_LEN 18 + +/* BT_ADDR_LE_STR_LEN + * Recommended length of user string buffer for Bluetooth LE address + * + * The recommended length guarantee the output of address conversion will not + * lose valuable information about address being processed. + */ + +#define BT_ADDR_LE_STR_LEN 27 + +/* BT_LE162HOST + * Convert 16-bit integer from little-endian to host endianness. + */ + +#ifdef CONFIG_ENDIAN_BIG +# define BT_LE162HOST(le) \ + ((((uint16_t)(le) >> 8) & 0xff) | (((uint16_t)(le) & 0xff) << 8)) +#else +# define BT_LE162HOST(le) (le) +#endif + +/* BT_HOST2LE16 + * Convert 16-bit integer from host endianness to little-endian. + */ + +#ifdef CONFIG_ENDIAN_BIG +# define BT_HOST2LE16(h) \ + ((((uint16_t)(h) >> 8) & 0xff) | (((uint16_t)(h) & 0xff) << 8)) +#else +# define BT_HOST2LE16(h) (h) +#endif + +/* Unaligned access */ + +#ifdef CONFIG_ENDIAN_BIG +# define BT_GETUINT16(p) \ + ((((uint16_t)(((FAR uint8_t *)(p))[1]) >> 8) & 0xff) | \ + (((uint16_t)(((FAR uint8_t *)(p))[0]) & 0xff) << 8)) +# define BT_PUTUINT16(p,v) \ + do \ + { \ + ((FAR uint8_t *)(p))[0] = ((uint16_t)(v) >> 8) & 0xff; \ + ((FAR uint8_t *)(p))[1] = ((uint16_t)(v) & 0xff) >> 8; \ + } \ + while (0) +#else +# define BT_GETUINT16(p) \ + ((((uint16_t)(((FAR uint8_t *)(p))[0]) >> 8) & 0xff) | \ + (((uint16_t)(((FAR uint8_t *)(p))[1]) & 0xff) << 8)) +# define BT_PUTUINT16(p,v) \ + do \ + { \ + ((FAR uint8_t *)(p))[0] = ((uint16_t)(v) & 0xff) >> 8; \ + ((FAR uint8_t *)(p))[1] = ((uint16_t)(v) >> 8) & 0xff; \ + } \ + while (0) +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Advertising API */ + +begin_packed_struct struct bt_eir_s +{ + uint8_t len; + uint8_t type; + uint8_t data[29]; +} end_packed_struct; + +/* Security level */ + +enum bt_security_e +{ + BT_SECURITY_LOW, /* No encryption and no authentication. */ + BT_SECURITY_MEDIUM, /* encryption and no authentication (no MITM). */ + BT_SECURITY_HIGH, /* encryption and authentication (MITM). */ + BT_SECURITY_FIPS, /* Authenticated LE Secure Connections and + * encryption. */ +}; + +/**************************************************************************** + * Name: bt_le_scan_cb_t + * + * Description: + * A function of this type will be called back when user application + * triggers active LE scan. The caller will populate all needed + * parameters based on data coming from scan result. + * Such function can be set by user when LE active scan API is used. + * + * Input Parameters: + * addr - Advertiser LE address and type. + * rssi - Strength of advertiser signal. + * adv_type - Type of advertising response from advertiser. + * adv_data - Address of buffer containing advertiser data. + * len - Length of advertiser data contained in buffer. + * + ****************************************************************************/ + +typedef CODE void bt_le_scan_cb_t(FAR const bt_addr_le_t *addr, int8_t rssi, + uint8_t adv_type, + FAR const uint8_t *adv_data, uint8_t len); + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_addr_to_str + * + * Description: + * Converts binary Bluetooth address to string. + * + * Input Parameters: + * addr - Address of buffer containing binary Bluetooth address. + * str - Address of user buffer with enough room to store formatted + * string containing binary address. + * len - Length of data to be copied to user string buffer. Refer to + * BT_ADDR_STR_LEN about recommended value. + * + * Returned Value: + * Number of successfully formatted bytes from binary address. + * + ****************************************************************************/ + +static inline int bt_addr_to_str(FAR const bt_addr_t *addr, FAR char *str, + size_t len) +{ + return snprintf(str, len, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + addr->val[5], addr->val[4], addr->val[3], + addr->val[2], addr->val[1], addr->val[0]); +} + +/**************************************************************************** + * Name: bt_addr_le_to_str + * + * Description: + * Converts binary LE Bluetooth address to string. + * + * Input Parameters: + * addr - Address of buffer containing binary LE Bluetooth address. + * user_buf - Address of user buffer with enough room to store + * formatted string containing binary LE address. + * len - Length of data to be copied to user string buffer. Refer to + * BT_ADDR_LE_STR_LEN about recommended value. + * + * Returned Value: + * Number of successfully formatted bytes from binary address. + * + ****************************************************************************/ + +static inline int bt_addr_le_to_str(const bt_addr_le_t *addr, char *str, + size_t len) +{ + char type[7]; + + switch (addr->type) + { + case BT_ADDR_LE_PUBLIC: + strcpy(type, "public"); + break; + + case BT_ADDR_LE_RANDOM: + strcpy(type, "random"); + break; + + default: + sprintf(type, "0x%02x", addr->type); + break; + } + + return snprintf(str, len, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X (%s)", + addr->val[5], addr->val[4], addr->val[3], + addr->val[2], addr->val[1], addr->val[0], type); +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_init + * + * Description: + * Initialize Bluetooth. Must be the called before anything else. + * + * Returned Value: + * Zero on success or (negative) error code otherwise. + * + ****************************************************************************/ + +int bt_init(void); + +/**************************************************************************** + * Name: bt_start_advertising + * + * Description: + * Set advertisement data, scan response data, advertisement parameters + * and start advertising. + * + * Input Parameters: + * type - Advertising type. + * ad - Data to be used in advertisement packets. + * sd - Data to be used in scan response packets. + * + * Returned Value: + * Zero on success or (negative) error code otherwise. + * + ****************************************************************************/ + +int bt_start_advertising(uint8_t type, FAR const struct bt_eir_s *ad, + FAR const struct bt_eir_s *sd); + +/**************************************************************************** + * Name: bt_stop_advertising + * + * Description: + * Stops ongoing advertising. + * + * Returned Value: + * Zero on success or (negative) error code otherwise. + * + ****************************************************************************/ + +int bt_stop_advertising(void); + +/**************************************************************************** + * Name: bt_start_scanning + * + * Description: + * Start LE scanning with and provide results through the specified + * callback. + * + * Input Parameters: + * filter_dups - Enable duplicate filtering (or not). + * cb - Callback to notify scan results. + * + * Returned Value: + * Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + * + ****************************************************************************/ + +int bt_start_scanning(uint8_t filter_dups, bt_le_scan_cb_t cb); + +/**************************************************************************** + * Name: bt_stop_scanning + * + * Description: + * Stops ongoing LE scanning. + * + * Returned Value: + * Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + * + ****************************************************************************/ + +int bt_stop_scanning(void); + +#endif /* __INCLUDE_NUTTX_WIRELESS_BT_CORE_H */ diff --git a/include/nuttx/wireless/bt_driver.h b/include/nuttx/wireless/bt_driver.h new file mode 100644 index 00000000000..38c5a068424 --- /dev/null +++ b/include/nuttx/wireless/bt_driver.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * wireless/bluetooth/bt_driver.h + * Bluetooth HCI driver API. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_WIRELESS_BT_DRIVER_H +#define __INCLUDE_NUTTX_WIRELESS_BT_DRIVER_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct bt_driver_s +{ + /* How much headroom is needed for HCI transport headers */ + + size_t head_reserve; + + /* Open the HCI transport */ + + CODE int (*open)(void); + + /* Send data to HCI */ + + CODE int (*send)(FAR struct bt_buf_s *buf); +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Register a new HCI driver to the Bluetooth stack */ + +int bt_driver_register(FAR struct bt_driver_s *drv); + +/* Unregister a previously registered HCI driver */ + +void bt_driver_unregister(FAR struct bt_driver_s *drv); + +/* Receive data from the controller/HCI driver */ + +void bt_recv(FAR struct bt_buf_s *buf); + +#endif /* __INCLUDE_NUTTX_WIRELESS_BT_DRIVER_H */ diff --git a/include/nuttx/wireless/bt_gatt.h b/include/nuttx/wireless/bt_gatt.h new file mode 100644 index 00000000000..d931c6539e8 --- /dev/null +++ b/include/nuttx/wireless/bt_gatt.h @@ -0,0 +1,1048 @@ +/**************************************************************************** + * wireless/bluetooth/bt_gatt.h + * Generic Attribute Profile handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_WIRELESS_BT_GATT_H +#define __INCLUDE_NUTTX_WIRELESS_BT_GATT_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* GATT attribute permission bitfield values */ + +/* BT_GATT_PERM_READ + * Attribute read permission. + */ + +#define BT_GATT_PERM_READ 0x01 + +/* BT_GATT_PERM_WRITE + * Attribute write permission. + */ + +#define BT_GATT_PERM_WRITE 0x02 + +/* BT_GATT_PERM_READ_ENCRYPT + * Attribute read permission with encryption. + * If set, requires encryption for read access. + */ + +#define BT_GATT_PERM_READ_ENCRYPT 0x04 + +/* BT_GATT_PERM_WRITE_ENCRYPT + * Attribute write permission with encryption. + * If set, requires encryption for write access. + */ + +#define BT_GATT_PERM_WRITE_ENCRYPT 0x08 + +/* BT_GATT_PERM_READ_AUTHEN + * Attribute read permission with authentication. + * If set, requires encryption using authenticated link-key for read access. + */ + +#define BT_GATT_PERM_READ_AUTHEN 0x10 + +/* BT_GATT_PERM_WRITE_AUTHEN + * Attribute write permission with authentication. + * If set, requires encryption using authenticated link-key for write + * access. + */ + +#define BT_GATT_PERM_WRITE_AUTHEN 0x20 + +/* BT_GATT_PERM_AUTHOR + * Attribute authorization permission. + */ + +#define BT_GATT_PERM_AUTHOR 0x40 + +/* GATT attribute flush flags */ + +/* BT_GATT_FLUSH_DISCARD + * Attribute flush discard flag. + */ + +#define BT_GATT_FLUSH_DISCARD 0x00 + +/* BT_GATT_FLUSH_DISCARD + * Attribute flush synchronize flag. + */ + +#define BT_GATT_FLUSH_SYNC 0x01 + +/* Characteristic Properties Bitfield values */ + +/* BT_GATT_CHRC_BROADCAST + * Characteristic broadcast property. + * If set, permits broadcasts of the Characteristic Value using Server + * Characteristic Configuration Descriptor. + */ + +#define BT_GATT_CHRC_BROADCAST 0x01 + +/* BT_GATT_CHRC_READ + * Characteristic read property. + * If set, permits reads of the Characteristic Value. + */ + +#define BT_GATT_CHRC_READ 0x02 + +/* BT_GATT_CHRC_WRITE_WITHOUT_RESP + * Characteristic write without response property. + * If set, permits write of the Characteristic Value without response. + */ + +#define BT_GATT_CHRC_WRITE_WITHOUT_RESP 0x04 + +/* BT_GATT_CHRC_WRITE + * Characteristic write with response property. + * If set, permits write of the Characteristic Value with response. + */ + +#define BT_GATT_CHRC_WRITE 0x08 + +/* BT_GATT_CHRC_NOTIFY + * Characteristic notify property. + * If set, permits notifications of a Characteristic Value without + * acknowledgment. + */ + +#define BT_GATT_CHRC_NOTIFY 0x10 + +/* BT_GATT_CHRC_INDICATE + * Characteristic indicate property. + * If set, permits indications of a Characteristic Value with + * acknowledgment. + */ + +#define BT_GATT_CHRC_INDICATE 0x20 + +/* BT_GATT_CHRC_AUTH + * Characteristic Authenticated Signed Writes property. + * If set, permits signed writes to the Characteristic Value. + */ + +#define BT_GATT_CHRC_AUTH 0x40 + +/* BT_GATT_CHRC_EXT_PROP + * Characteristic Extended Properties property. + * If set, additional characteristic properties are defined in the + * Characteristic Extended Properties Descriptor. + */ + +#define BT_GATT_CHRC_EXT_PROP 0x80 + +/* Characteristic Extended Properties Bitfield values */ + +#define BT_GATT_CEP_RELIABLE_WRITE 0x0001 +#define BT_GATT_CEP_WRITABLE_AUX 0x0002 + +/* Client Characteristic Configuration Values */ + +/* BT_GATT_CCC_NOTIFY + * Client Characteristic Configuration Notification. + * If set, changes to Characteristic Value shall be notified. + */ + +#define BT_GATT_CCC_NOTIFY 0x0001 + +/* BT_GATT_CCC_INDICATE + * Client Characteristic Configuration Indication. + * If set, changes to Characteristic Value shall be indicated. + */ + +#define BT_GATT_CCC_INDICATE 0x0002 + +/* BT_GATT_SERVICE + * Helper macro to declare a service attribute. + * + * Arguments: + * _handle - Service attribute handle. + * _uuid - Service attribute type. + * _data - Service attribute value. + */ + +#define BT_GATT_SERVICE(_handle, _uuid, _service) \ +{ \ + .handle = _handle, \ + .uuid = _uuid, \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_service, \ + .user_data = _service, \ +} + +/* BT_GATT_PRIMARY_SERVICE + * Helper macro to declare a primary service attribute. + * + * Arguments: + * _handle - Service attribute handle. + * _service - Service attribute value. + */ + +#define BT_GATT_PRIMARY_SERVICE(_handle, _service) \ +{ \ + .handle = _handle, \ + .uuid = (&(struct bt_uuid_s) \ + { \ + BT_UUID_16, \ + { \ + BT_UUID_GATT_PRIMARY \ + } \ + }), \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_service, \ + .user_data = _service, \ +} + +/* BT_GATT_SECONDARY_SERVICE + * Helper macro to declare a secondary service attribute. + * + * Arguments: + * _handle - Service attribute handle. + * _service - Service attribute value. + */ + +#define BT_GATT_SECONDARY_SERVICE(_handle, _service) \ +{ \ + .handle = _handle, \ + .uuid = (&(struct bt_uuid_s) \ + { \ + BT_UUID_16, \ + { \ + BT_UUID_GATT_SECONDARY \ + } \ + }), \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_service, \ + .user_data = _service, \ +} + +/* BT_GATT_INCLUDE_SERVICE + * Helper macro to declare a include service attribute. + * + * Arguments: + * _handle Service attribute handle. + * _service Service attribute value. + */ + +#define BT_GATT_INCLUDE_SERVICE(_handle, _service) \ +{ \ + .handle = _handle, \ + .uuid = (&(struct bt_uuid_s) \ + { \ + BT_UUID_16, \ + { \ + BT_UUID_GATT_INCLUDE \ + } \ + }), \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_included, \ + .user_data = _service, \ +} + +/* BT_GATT_CHARACTERISTIC + * Helper macro to declare a characteristic attribute. + * + * Arguments: + * _handle - Characteristic attribute handle. + * _value - Characteristic attribute value. + */ + +#define BT_GATT_CHARACTERISTIC(_handle, _value) \ +{ \ + .handle = _handle, \ + .uuid = (&(struct bt_uuid_s) \ + { \ + BT_UUID_16, \ + { \ + BT_UUID_GATT_CHRC \ + } \ + }), \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_chrc, \ + .user_data = _value, \ +} + +/* BT_GATT_CCC + * Helper macro to declare a CCC attribute. + * + * Arguments: + * _handle - Descriptor attribute handle. + * _value_handle - Characteristic attribute value handle. + * _cfg - Initial configuration. + * _cfg_changed - Configuration changed callback. + */ + +#define BT_GATT_CCC(_handle, _value_handle, _cfg, _cfg_changed) \ +{ \ + .handle = _handle, \ + .uuid = (&(struct bt_uuid_s) \ + { \ + BT_UUID_16, \ + { \ + BT_UUID_GATT_CCC \ + } \ + }), \ + .perm = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, \ + .read = bt_gatt_attr_read_ccc, \ + .write = bt_gatt_attr_write_ccc, \ + .user_data = (&(struct _bt_gatt_ccc) \ + { \ + .cfg = _cfg, \ + .cfg_len = ARRAY_SIZE(_cfg), \ + .value_handle = _value_handle, \ + .cfg_changed = _cfg_changed, \ + }),\ +} + +/* BT_GATT_CEP + * Helper macro to declare a CEP attribute. + * + * Arguments: + * _handle Descriptor attribute handle. + * _value Descriptor attribute value. + */ + +#define BT_GATT_CEP(_handle, _value) \ +{ \ + .handle = _handle, \ + .uuid = (&(struct bt_uuid_s) \ + { \ + BT_UUID_16, \ + { \ + BT_UUID_GATT_CEP \ + } \ + }), \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_cep, \ + .user_data = _value, \ +} + +/* BT_GATT_DESCRIPTOR + * Helper macro to declare a descriptor attribute. + * + * Arguments: + * _handle - Descriptor attribute handle. + * _value - Descriptor attribute value. + * _perm - Descriptor attribute access permissions. + * _read - Descriptor attribute read callback. + * _write - Descriptor attribute write callback. + * _value - Descriptor attribute value. + */ + +#define BT_GATT_DESCRIPTOR(_handle, _uuid, _perm, _read, _write, _value) \ +{ \ + .handle = _handle, \ + .uuid = _uuid, \ + .perm = _perm, \ + .read = _read, \ + .write = _write, \ + .user_data = _value, \ +} + +/* BT_GATT_LONG_DESCRIPTOR + * Helper macro to declare a descriptor attribute. + * + * Arguments: + * _handle - Descriptor attribute handle. + * _value - Descriptor attribute value. + * _perm - Descriptor attribute access permissions. + * _read - Descriptor attribute read callback. + * _write - Descriptor attribute write callback. + * _flush - Descriptor attribute flush callback. + * _value - Descriptor attribute value. + */ + +#define BT_GATT_LONG_DESCRIPTOR(_handle, _uuid, _perm, _read, _write, _flush, \ + _value) \ +{ \ + .handle = _handle, \ + .uuid = _uuid, \ + .perm = _perm, \ + .read = _read, \ + .write = _write, \ + .flush = _flush, \ + .user_data = _value, \ +} + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum bt_gatt_iter_e +{ + BT_GATT_ITER_STOP = 0, + BT_GATT_ITER_CONTINUE, +}; + +/* Attribute iterator callback. + * + * Input Parameters: + * attr - Attribute found. + * user_data - Data given. + * + * Returned Value: + * BT_GATT_ITER_CONTINUE if should continue to the next attribute + * or BT_GATT_ITER_STOP to stop. + */ + +struct bt_gatt_attr_s; /* Forward reference */ + +typedef CODE uint8_t + (*bt_gatt_attr_func_t)(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); + +/* Response callback function + * + * Input Parameters: + * conn - Connection object. + * err - Error code. + */ + +struct bt_conn_s; /* Forward reference */ + +typedef void (*bt_gatt_rsp_func_t)(FAR struct bt_conn_s *conn, uint8_t err); + +/* Read callback function + * + * Input Parameters: + * conn - Connection object. + * err - Error code. + * data - Attribute value data. + * length - Attribute value length. + */ + +typedef CODE void (*bt_gatt_read_func_t)(FAR struct bt_conn_s *conn, + int err, FAR const void *data, + uint16_t length); + +/* GATT Attribute structure. */ + +struct bt_gatt_attr_s +{ + /* Attribute UUID */ + + FAR const struct bt_uuid_s *uuid; + + /* Attribute read callback */ + + CODE int (*read)(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, FAR void *buf, + uint8_t len, uint16_t offset); + + /* Attribute write callback */ + + CODE int (*write)(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR const void *buf, uint8_t len, uint16_t offset); + + /* Attribute flush callback */ + + CODE int (*flush)(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + uint8_t flags); + + /* Attribute user data */ + + FAR void *user_data; + + /* Attribute handle */ + + uint16_t handle; + + /* Attribute permissions */ + + uint8_t perm; +}; + +/* Service Attribute Value. */ + +struct bt_gatt_service_s +{ + /* Service UUID. */ + + FAR const struct bt_uuid_s *uuid; +}; + +/* Include Attribute Value. */ + +struct bt_gatt_include_s +{ + /* Service UUID. */ + + FAR const struct bt_uuid_s *uuid; + + /* Service start handle. */ + + uint16_t start_handle; + + /* Service end handle. */ + + uint16_t end_handle; +}; + +/* Characteristic Attribute Value. */ + +struct bt_gatt_chrc_s +{ + /* Characteristic UUID. */ + + FAR const struct bt_uuid_s *uuid; + + /* Characteristic value handle. */ + + uint16_t value_handle; + + /* Characteristic properties. */ + + uint8_t properties; +}; + +/* Characteristic Extended Properties Attribute Value. */ + +struct bt_gatt_cep_s +{ + /* Characteristic Extended properties */ + + uint16_t properties; +}; + +/* Characteristic User Description Attribute Value. */ + +struct bt_gatt_cud_s +{ + /* Characteristic User Description string. */ + + FAR char *string; +}; + +/* Client Characteristic Configuration Attribute Value */ + +struct bt_gatt_ccc_s +{ + /* Client Characteristic Configuration flags */ + + uint16_t flags; +}; + +/* GATT CCC configuration entry. */ + +struct bt_gatt_ccc_cfg_s +{ + /* Config peer address. */ + + bt_addr_le_t peer; + + /* Config peer value. */ + + uint16_t value; + + /* Config valid flag. */ + + uint8_t valid; +}; + +/* Internal representation of CCC value */ + +struct _bt_gatt_ccc_s +{ + FAR struct bt_gatt_ccc_cfg_s *cfg; + size_t cfg_len; + uint16_t value; + uint16_t value_handle; + CODE void (*cfg_changed)(uint16_t value); +}; + +/* GATT Discover Primary parameters */ + +struct bt_gatt_discover_params_s +{ + /* Discover UUID type */ + + FAR struct bt_uuid_s *uuid; + + /* Discover attribute callback */ + + bt_gatt_attr_func_t func; + + /* Discover destroy callback */ + + CODE void (*destroy)(FAR void *user_data); + + /* Discover start handle */ + + uint16_t start_handle; + + /* Discover end handle */ + + uint16_t end_handle; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Server API */ + +/**************************************************************************** + * Name: bt_gatt_register + * + * Description: + * Register GATT attribute database table. Applications can make use of + * macros such as BT_GATT_PRIMARY_SERVICE, BT_GATT_CHARACTERISTIC, + * BT_GATT_DESCRIPTOR, etc. + * + * Input Parameters: + * attrs - Database table containing the available attributes. + * count - Size of the database table. + * + ****************************************************************************/ + +void bt_gatt_register(FAR const struct bt_gatt_attr_s *attrs, size_t count); + +/**************************************************************************** + * Name: bt_gatt_foreach_attr + * + * Description: + * Iterate attributes in the given range. + * + * Input Parameters: + * start_handle - Start handle. + * end_handle - End handle. + * func - Callback function. + * user_data - Data to pass to the callback. + * + ****************************************************************************/ + +void bt_gatt_foreach_attr(uint16_t start_handle, uint16_t end_handle, + bt_gatt_attr_func_t func, FAR void *user_data); + +/**************************************************************************** + * Name: bt_gatt_attr_read + * + * Description: + * Read attribute value storing the result into buffer. + * + * Input Parameters: + * conn - Connection object. + * attr - Attribute to read. + * buf - Buffer to store the value. + * buf_len - Buffer length. + * offset - Start offset. + * value - Attribute value. + * value_len - Length of the attribute value. + * + * Returned Value: + * int number of bytes read in case of success or negative values in + * case of error. + * + ****************************************************************************/ + +int bt_gatt_attr_read(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR void *buf, uint8_t buf_len, uint16_t offset, + FAR const void *value, uint8_t value_len); + +/**************************************************************************** + * Name: bt_gatt_attr_read_service + * + * Description: + * Read service attribute value storing the result into buffer after + * encoding it. + * NOTE: Only use this with attributes which user_data is a bt_uuid_s. + * + * Input Parameters: + * conn - Connection object. + * attr - Attribute to read. + * buf - Buffer to store the value read. + * len - Buffer length. + * offset - Start offset. + * + * Returned Value: + * int number of bytes read in case of success or negative values in + * case of error. + * + ****************************************************************************/ + +int bt_gatt_attr_read_service(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR void *buf, uint8_t len, uint16_t offset); + +/**************************************************************************** + * Name: bt_gatt_attr_read_included + * + * Description: + * Read include service attribute value storing the result into buffer after + * encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_include. + * + * Input Parameters: + * conn - Connection object. + * attr - Attribute to read. + * buf - Buffer to store the value read. + * len - Buffer length. + * offset - Start offset. + * + * Returned Value: + * int number of bytes read in case of success or negative values in + * case of error. + * + ****************************************************************************/ + +int bt_gatt_attr_read_included(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR void *buf, uint8_t len, uint16_t offset); + +/**************************************************************************** + * Name: bt_gatt_attr_read_chrc + * + * Description: + * Read characteristic attribute value storing the result into buffer after + * encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_chrc. + * + * Input Parameters: + * conn - Connection object. + * attr - Attribute to read. + * buf - Buffer to store the value read. + * len - Buffer length. + * offset - Start offset. + * + * Returned Value: + * number of bytes read in case of success or negative values in + * case of error. + * + ****************************************************************************/ + +int bt_gatt_attr_read_chrc(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, FAR void *buf, + uint8_t len, uint16_t offset); + +/**************************************************************************** + * Name: bt_gatt_attr_read_ccc + * + * Description: + * Read CCC attribute value storing the result into buffer after + * encoding it. + * NOTE: Only use this with attributes which user_data is a _bt_gatt_ccc. + * + * Input Parameters: + * conn - Connection object. + * attr - Attribute to read. + * buf - Buffer to store the value read. + * len - Buffer length. + * offset - Start offset. + * + * Returned Value: + * number of bytes read in case of success or negative values in + * case of error. + * + ****************************************************************************/ + +int bt_gatt_attr_read_ccc(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, FAR void *buf, + uint8_t len, uint16_t offset); + +/**************************************************************************** + * Name: bt_gatt_attr_write_ccc + * + * Description: + * Write value in the buffer into CCC attribute. + * NOTE: Only use this with attributes which user_data is a _bt_gatt_ccc. + * + * Input Parameters: + * conn - Connection object. + * attr - Attribute to read. + * buf - Buffer to store the value read. + * len - Buffer length. + * offset - Start offset. + * + * Returned Value: + * number of bytes written in case of success or negative values in + * case of error. + * + ****************************************************************************/ + +int bt_gatt_attr_write_ccc(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR const void *buf, uint8_t len, uint16_t offset); + +/**************************************************************************** + * Name: bt_gatt_attr_read_cep + * + * Description: + * Read CEP attribute value storing the result into buffer after + * encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_cep. + * + * Input Parameters: + * conn - Connection object + * attr - Attribute to read + * buf - Buffer to store the value read + * len - Buffer length + * offset - Start offset + * + * Returned Value: + * number of bytes read in case of success or negative values in + * case of error. + * + ****************************************************************************/ + +int bt_gatt_attr_read_cep(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, FAR void *buf, + uint8_t len, uint16_t offset); + +/**************************************************************************** + * Name: bt_gatt_notify + * + * Description: + * Send notification of attribute value change. + * Note: This function should only be called if CCC is declared with + * BT_GATT_CCC otherwise it cannot find a valid peer configuration. + * + * Input Parameters: + * handle - Attribute handle. + * value - Attribute value. + * len - Attribute value length. + * + ****************************************************************************/ + +void bt_gatt_notify(uint16_t handle, FAR const void *data, size_t len); + +/**************************************************************************** + * Name: bt_gatt_connected + * + * Description: + * Connected callback. + * + * Input Parameters: + * conn - Connection object. + * + ****************************************************************************/ + +void bt_gatt_connected(FAR struct bt_conn_s *conn); + +/**************************************************************************** + * Name: bt_gatt_disconnected + * + * Description: + * Disconnected callback. + * + * Input Parameters: + * conn - Connection object. + * + ****************************************************************************/ + +void bt_gatt_disconnected(FAR struct bt_conn_s *conn); + +/* Client API */ + +/**************************************************************************** + * Name: bt_gatt_exchange_mtu + * + * Description: + * This client procedure can be used to set the MTU to the maximum possible + * size the buffers can hold. + * NOTE: Can only be used once per connection. + * + * Input Parameters: + * conn - Connection object. + * + ****************************************************************************/ + +int bt_gatt_exchange_mtu(FAR struct bt_conn_s *conn, bt_gatt_rsp_func_t func); + +/**************************************************************************** + * Name: bt_gatt_discover + * + * Description: + * This procedure is used by a client to discover a specific primary service on + * a server when only the Service UUID is known. + * + * For each attribute found the callback is called which can then decide + * whether to continue discovering or stop. + * + * Input Parameters: + * conn - Connection object. + * params - Discover parameters. + * + * Returned Value: + * Zero in case of success or negative value in case of error. + * + ****************************************************************************/ + +int bt_gatt_discover(FAR struct bt_conn_s *conn, + FAR struct bt_gatt_discover_params_s *params); + +/**************************************************************************** + * Name: bt_gatt_discover_characteristic + * + * Description: + * This procedure is used by a client to discover all characteristics on a + * server. + * Note: In case the UUID is set in the parameter it will be matched against + * the attributes found before calling the function callback. + * + * For each attribute found the callback is called which can then decide + * whether to continue discovering or stop. + * + * Input Parameters: + * conn - Connection object. + * params - Discover parameters. + * + * Returned Value: + * Zero in case of success or negative value in case of error. + * + ****************************************************************************/ + +int bt_gatt_discover_characteristic(FAR struct bt_conn_s *conn, + FAR struct bt_gatt_discover_params_s *params); + +/**************************************************************************** + * Name: bt_gatt_discover_descriptor + * + * Description: + * This procedure is used by a client to discover descriptors on a server. + * Note: In case the UUID is set in the parameter it will be matched against + * the attributes found before calling the function callback. + * + * For each attribute found the callback is called which can then decide + * whether to continue discovering or stop. + * + * Input Parameters: + * conn - Connection object. + * params - Discover parameters. + * + * Returned Value: + * Zero in case of success or negative value in case of error. + * + ****************************************************************************/ + +int bt_gatt_discover_descriptor(FAR struct bt_conn_s *conn, + FAR struct bt_gatt_discover_params_s *params); + +/**************************************************************************** + * Name: bt_gatt_read + * + * Description: + * This procedure read the attribute value and return it to the callback. + * + * Input Parameters: + * conn - Connection object. + * handle - Attribute handle. + * offset - Attribute data offset. + * func - Callback function. + * + * Returned Value: + * Zero in case of success or negative value in case of error. + * + ****************************************************************************/ + +int bt_gatt_read(FAR struct bt_conn_s *conn, uint16_t handle, uint16_t offset, + bt_gatt_read_func_t func); + +/**************************************************************************** + * Name: + * + * Description: + * This procedure write the attribute value and return the result in the + * callback in case it is set. + * + * Input Parameters: + * conn - Connection object. + * handle - Attribute handle. + * data - Data to be written. + * length - Data length. + * func - Callback function. + * + * Returned Value: + * Zero in case of success or negative value in case of error. + * + ****************************************************************************/ + +int bt_gatt_write(FAR struct bt_conn_s *conn, uint16_t handle, + FAR const void *data, uint16_t length, + bt_gatt_rsp_func_t func); + +/**************************************************************************** + * Name: bt_gatt_cancel + * + * Description: + * Cancel GATT pending request + * + * Input Parameters: + * conn - Connection object. + * + ****************************************************************************/ + +void bt_gatt_cancel(FAR struct bt_conn_s *conn); + +/**************************************************************************** + * Name: bt_gatt_read_multiple + * + * Description: + * Routine to be used to retrieve set of attributes values determined by + * set of handles in one call. + * + * Input Parameters: + * conn - Connection object. + * handles - Set of valid handles to attributes. + * count - Number of handles to be read. + * func - User callback routine to get retrieved values. + * + * Returned Value: + * Zero in case of success or negative value in case of error. + * + ****************************************************************************/ + +int bt_gatt_read_multiple(FAR struct bt_conn_s *conn, + FAR const uint16_t *handles, size_t count, + bt_gatt_read_func_t func); + +#endif /* __INCLUDE_NUTTX_WIRELESS_BT_GATT_H */ diff --git a/include/nuttx/wireless/bt_hci.h b/include/nuttx/wireless/bt_hci.h new file mode 100644 index 00000000000..b633a0984b3 --- /dev/null +++ b/include/nuttx/wireless/bt_hci.h @@ -0,0 +1,467 @@ +/**************************************************************************** + * wireless/bluetooth/bt_hci.h + * Bluetooth Host Control Interface definitions. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_WIRELESS_BT_HCI_H +#define __INCLUDE_NUTTX_WIRELESS_BT_HCI_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BT_ADDR_LE_PUBLIC 0x00 +#define BT_ADDR_LE_RANDOM 0x01 + +#define BT_ADDR_ANY (&(bt_addr_t) {{0, 0, 0, 0, 0, 0}}) +#define BT_ADDR_LE_ANY (&(bt_addr_le_t) { 0, {0, 0, 0, 0, 0, 0}}) + +/* HCI Error Codes */ + +#define BT_HCI_ERR_AUTHENTICATION_FAIL 0x05 +#define BT_HCI_ERR_REMOTE_USER_TERM_CONN 0x13 +#define BT_HCI_ERR_UNSUPP_REMOTE_FEATURE 0x1a +#define BT_HCI_ERR_PAIRING_NOT_SUPPORTED 0x29 +#define BT_HCI_ERR_UNACCEPT_CONN_PARAMS 0x3b + +/* EIR/AD definitions */ + +#define BT_EIR_FLAGS 0x01 /* AD flags */ +#define BT_EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ +#define BT_EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ +#define BT_EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */ +#define BT_EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ +#define BT_EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */ +#define BT_EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ +#define BT_EIR_NAME_COMPLETE 0x09 /* Complete name */ +#define BT_EIR_TX_POWER 0x0a /* Tx Power */ +#define BT_EIR_SOLICIT16 0x14 /* Solicit UUIDs, 16-bit */ +#define BT_EIR_SOLICIT128 0x15 /* Solicit UUIDs, 128-bit */ +#define BT_EIR_SVC_DATA16 0x16 /* Service data, 16-bit UUID */ +#define BT_EIR_GAP_APPEARANCE 0x19 /* GAP appearance */ +#define BT_EIR_SOLICIT32 0x1f /* Solicit UUIDs, 32-bit */ +#define BT_EIR_SVC_DATA32 0x20 /* Service data, 32-bit UUID */ +#define BT_EIR_SVC_DATA128 0x21 /* Service data, 128-bit UUID */ +#define BT_EIR_MANUFACTURER_DATA 0xff /* Manufacturer Specific Data */ + +#define BT_LE_AD_GENERAL 0x02 /* General Discoverable */ +#define BT_LE_AD_NO_BREDR 0x04 /* BR/EDR not supported */ + +#define bt_acl_handle(h) ((h) & 0x0fff) + +/* LMP features */ + +#define BT_LMP_NO_BREDR 0x20 +#define BT_LMP_LE 0x40 + +/* LE features */ +#define BT_HCI_LE_ENCRYPTION 0x01 + +/* OpCode Group Fields */ + +#define BT_OGF_LINK_CTRL 0x01 +#define BT_OGF_BASEBAND 0x03 +#define BT_OGF_INFO 0x04 +#define BT_OGF_LE 0x08 + +/* Construct OpCode from OGF and OCF */ + +#define BT_OP(ogf, ocf) ((ocf) | ((ogf) << 10)) + +#define BT_HCI_OP_DISCONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0006) +#define BT_HCI_OP_SET_EVENT_MASK BT_OP(BT_OGF_BASEBAND, 0x0001) +#define BT_HCI_OP_RESET BT_OP(BT_OGF_BASEBAND, 0x0003) +#define BT_HCI_OP_SET_CTL_TO_HOST_FLOW BT_OP(BT_OGF_BASEBAND, 0x0031) +#define BT_HCI_OP_HOST_BUFFER_SIZE BT_OP(BT_OGF_BASEBAND, 0x0033) +#define BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS BT_OP(BT_OGF_BASEBAND, 0x0035) +#define BT_HCI_OP_LE_WRITE_LE_HOST_SUPP BT_OP(BT_OGF_BASEBAND, 0x006d) +#define BT_HCI_OP_READ_LOCAL_VERSION_INFO BT_OP(BT_OGF_INFO, 0x0001) +#define BT_HCI_OP_READ_LOCAL_FEATURES BT_OP(BT_OGF_INFO, 0x0003) +#define BT_HCI_OP_READ_BUFFER_SIZE BT_OP(BT_OGF_INFO, 0x0005) +#define BT_HCI_OP_READ_BD_ADDR BT_OP(BT_OGF_INFO, 0x0009) +#define BT_HCI_OP_LE_READ_BUFFER_SIZE BT_OP(BT_OGF_LE, 0x0002) +#define BT_HCI_OP_LE_READ_LOCAL_FEATURES BT_OP(BT_OGF_LE, 0x0003) + +/* Advertising types */ + +#define BT_LE_ADV_IND 0x00 +#define BT_LE_ADV_DIRECT_IND 0x01 +#define BT_LE_ADV_SCAN_IND 0x02 +#define BT_LE_ADV_NONCONN_IND 0x03 +#define BT_LE_ADV_SCAN_RSP 0x04 + +#define BT_HCI_OP_LE_SET_ADV_PARAMETERS BT_OP(BT_OGF_LE, 0x0006) +#define BT_HCI_OP_LE_SET_ADV_DATA BT_OP(BT_OGF_LE, 0x0008) +#define BT_HCI_OP_LE_SET_SCAN_RSP_DATA BT_OP(BT_OGF_LE, 0x0009) +#define BT_HCI_OP_LE_SET_ADV_ENABLE BT_OP(BT_OGF_LE, 0x000a) + +/* Scan types */ + +#define BT_HCI_OP_LE_SET_SCAN_PARAMS BT_OP(BT_OGF_LE, 0x000b) +# define BT_LE_SCAN_PASSIVE 0x00 +# define BT_LE_SCAN_ACTIVE 0x01 +#define BT_HCI_OP_LE_SET_SCAN_ENABLE BT_OP(BT_OGF_LE, 0x000c) +# define BT_LE_SCAN_DISABLE 0x00 +# define BT_LE_SCAN_ENABLE 0x01 +# define BT_LE_SCAN_FILTER_DUP_DISABLE 0x00 +# define BT_LE_SCAN_FILTER_DUP_ENABLE 0x01 +#define BT_HCI_OP_LE_CREATE_CONN BT_OP(BT_OGF_LE, 0x000d) +#define BT_HCI_OP_LE_CREATE_CONN_CANCEL BT_OP(BT_OGF_LE, 0x000e) +#define BT_HCI_OP_LE_CONN_UPDATE BT_OP(BT_OGF_LE, 0x0013) +#define BT_HCI_OP_LE_ENCRYPT BT_OP(BT_OGF_LE, 0x0017) +#define BT_HCI_OP_LE_RAND BT_OP(BT_OGF_LE, 0x0018) +#define BT_HCI_OP_LE_START_ENCRYPTION BT_OP(BT_OGF_LE, 0x0019) +#define BT_HCI_OP_LE_LTK_REQ_REPLY BT_OP(BT_OGF_LE, 0x001a) +#define BT_HCI_OP_LE_LTK_REQ_NEG_REPLY BT_OP(BT_OGF_LE, 0x001b) + +/* Event definitions */ + +#define BT_HCI_EVT_DISCONN_COMPLETE 0x05 +#define BT_HCI_EVT_ENCRYPT_CHANGE 0x08 +#define BT_HCI_EVT_CMD_COMPLETE 0x0e +#define BT_HCI_EVT_CMD_STATUS 0x0f +#define BT_HCI_EVT_NUM_COMPLETED_PACKETS 0x13 +#define BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE 0x30 +#define BT_HCI_EVT_LE_META_EVENT 0x3e +#define BT_HCI_EVT_LE_CONN_COMPLETE 0x01 +# define BT_HCI_ROLE_MASTER 0x00 +# define BT_HCI_ROLE_SLAVE 0x01 +#define BT_HCI_EVT_LE_ADVERTISING_REPORT 0x02 +#define BT_HCI_EVT_LE_LTK_REQUEST 0x05 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct bt_addr_s +{ + uint8_t val[6]; +} bt_addr_t; + +typedef struct bt_addr_le_s +{ + uint8_t type; + uint8_t val[6]; +} bt_addr_le_t; + +begin_packed_struct struct bt_hci_evt_hdr_s +{ + uint8_t evt; + uint8_t len; +} end_packed_struct; + +begin_packed_struct struct bt_hci_acl_hdr_s +{ + uint16_t handle; + uint16_t len; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cmd_hdr_s +{ + uint16_t opcode; + uint8_t param_len; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_disconnect_s +{ + uint16_t handle; + uint8_t reason; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_set_event_mask_s +{ + uint8_t events[8]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_host_buffer_size_s +{ + uint16_t acl_mtu; + uint8_t sco_mtu; + uint16_t acl_pkts; + uint16_t sco_pkts; +} end_packed_struct; + +begin_packed_struct struct bt_hci_handle_count_s +{ + uint16_t handle; + uint16_t count; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_host_num_completed_packets_s +{ + uint8_t num_handles; + struct bt_hci_handle_count_s h[0]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_write_le_host_supp_s +{ + uint8_t le; + uint8_t simul; +} end_packed_struct; + +begin_packed_struct struct bt_hci_rp_read_local_version_info_s +{ + uint8_t status; + uint8_t hci_version; + uint16_t hci_revision; + uint8_t lmp_version; + uint16_t manufacturer; + uint16_t lmp_subversion; +} end_packed_struct; + +begin_packed_struct struct bt_hci_rp_read_local_features_s +{ + uint8_t status; + uint8_t features[8]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_rp_read_buffer_size_s +{ + uint8_t status; + uint16_t acl_max_len; + uint8_t sco_max_len; + uint16_t acl_max_num; + uint16_t sco_max_num; +} end_packed_struct; + +begin_packed_struct struct bt_hci_rp_read_bd_addr_s +{ + uint8_t status; + bt_addr_t bdaddr; +} end_packed_struct; + +begin_packed_struct struct bt_hci_rp_le_read_buffer_size_s +{ + uint8_t status; + uint16_t le_max_len; + uint8_t le_max_num; +} end_packed_struct; + +begin_packed_struct struct bt_hci_rp_le_read_local_features_s +{ + uint8_t status; + uint8_t features[8]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_set_adv_parameters_s +{ + uint16_t min_interval; + uint16_t max_interval; + uint8_t type; + uint8_t own_addr_type; + bt_addr_le_t direct_addr; + uint8_t channel_map; + uint8_t filter_policy; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_set_adv_data_s +{ + uint8_t len; + uint8_t data[31]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_set_scan_rsp_data_s +{ + uint8_t len; + uint8_t data[31]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_set_adv_enable_s +{ + uint8_t enable; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_set_scan_params_s +{ + uint8_t scan_type; + uint16_t interval; + uint16_t window; + uint8_t addr_type; + uint8_t filter_policy; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_set_scan_enable_s +{ + uint8_t enable; + uint8_t filter_dup; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_create_conn_s +{ + uint16_t scan_interval; + uint16_t scan_window; + uint8_t filter_policy; + bt_addr_le_t peer_addr; + uint8_t own_addr_type; + uint16_t conn_interval_min; + uint16_t conn_interval_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +} end_packed_struct; + +begin_packed_struct struct hci_cp_le_conn_update_s +{ + uint16_t handle; + uint16_t conn_interval_min; + uint16_t conn_interval_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_encrypt_s +{ + uint8_t key[16]; + uint8_t plaintext[16]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_rp_le_encrypt_s +{ + uint8_t status; + uint8_t enc_data[16]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_rp_le_rand_s +{ + uint8_t status; + uint8_t rand[8]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_start_encryption_s +{ + uint16_t handle; + uint64_t rand; + uint16_t ediv; + uint8_t ltk[16]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_ltk_req_reply_s +{ + uint16_t handle; + uint8_t ltk[16]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_cp_le_ltk_req_neg_reply_s +{ + uint16_t handle; +} end_packed_struct; + +/* Event definitions */ + +begin_packed_struct struct bt_hci_evt_disconn_complete_s +{ + uint8_t status; + uint16_t handle; + uint8_t reason; +} end_packed_struct; + +begin_packed_struct struct bt_hci_evt_encrypt_change_s +{ + uint8_t status; + uint16_t handle; + uint8_t encrypt; +} end_packed_struct; + +begin_packed_struct struct hci_evt_cmd_complete_s +{ + uint8_t ncmd; + uint16_t opcode; +} end_packed_struct; + +begin_packed_struct struct bt_hci_evt_cmd_status_s +{ + uint8_t status; + uint8_t ncmd; + uint16_t opcode; +} end_packed_struct; + +begin_packed_struct struct bt_hci_evt_num_completed_packets_s +{ + uint8_t num_handles; + struct bt_hci_handle_count_s h[0]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_evt_encrypt_key_refresh_complete_s +{ + uint8_t status; + uint16_t handle; +} end_packed_struct; + +begin_packed_struct struct bt_hci_evt_le_meta_event_s +{ + uint8_t subevent; +} end_packed_struct; + +begin_packed_struct struct bt_hci_evt_le_conn_complete_s +{ + uint8_t status; + uint16_t handle; + uint8_t role; + bt_addr_le_t peer_addr; + uint16_t interval; + uint16_t latency; + uint16_t supv_timeout; + uint8_t clock_accuracy; +} end_packed_struct; + +begin_packed_struct struct bt_hci_ev_le_advertising_info_s +{ + uint8_t evt_type; + bt_addr_le_t addr; + uint8_t length; + uint8_t data[0]; +} end_packed_struct; + +begin_packed_struct struct bt_hci_evt_le_ltk_request_s +{ + uint16_t handle; + uint64_t rand; + uint16_t ediv; +} end_packed_struct; + +#endif /* __INCLUDE_NUTTX_WIRELESS_BT_HCI_H */ diff --git a/include/nuttx/wireless/bt_uuid.h b/include/nuttx/wireless/bt_uuid.h new file mode 100644 index 00000000000..2548ce9586a --- /dev/null +++ b/include/nuttx/wireless/bt_uuid.h @@ -0,0 +1,220 @@ +/**************************************************************************** + * wireless/bluetooth/bt_uuid.h + * B Bluetooth UUID handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_WIRELESS_BT_UUID_H +#define __INCLUDE_NUTTX_WIRELESS_BT_UUID_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/** @def BBT_UUID_GAP + * @brief Generic Access + */ +#define BT_UUID_GAP 0x1800 +/** @def BBT_UUID_GATT + * @brief Generic Attribute + */ +#define BT_UUID_GATT 0x1801 +/** @def BBT_UUID_CTS + * @brief Current Time Service + */ +#define BT_UUID_CTS 0x1805 +/** @def BBT_UUID_DIS + * @brief Device Information Service + */ +#define BT_UUID_DIS 0x180a +/** @def BBT_UUID_HRS + * @brief Heart Rate Service + */ +#define BT_UUID_HRS 0x180d +/** @def BBT_UUID_BAS + * @brief Battery Service + */ +#define BT_UUID_BAS 0x180f +/** @def BT_UUID_GATT_PRIMARY + * @brief GATT Primary Service + */ +#define BT_UUID_GATT_PRIMARY 0x2800 +/** @def BT_UUID_GATT_SECONDARY + * @brief GATT Secondary Service + */ +#define BT_UUID_GATT_SECONDARY 0x2801 +/** @def BT_UUID_GATT_INCLUDE + * @brief GATT Include Service + */ +#define BT_UUID_GATT_INCLUDE 0x2802 +/** @def BT_UUID_GATT_CHRC + * @brief GATT Characteristic + */ +#define BT_UUID_GATT_CHRC 0x2803 +/** @def BT_UUID_GATT_CEP + * @brief GATT Characteristic Extended Properties + */ +#define BT_UUID_GATT_CEP 0x2900 +/** @def BT_UUID_GATT_CUD + * @brief GATT Characteristic User Description + */ +#define BT_UUID_GATT_CUD 0x2901 +/** @def BT_UUID_GATT_CCC + * @brief GATT Client Characteristic Configuration + */ +#define BT_UUID_GATT_CCC 0x2902 +/** @def BT_UUID_GAP_DEVICE_NAME + * @brief GAP Characteristic Device Name + */ +#define BT_UUID_GAP_DEVICE_NAME 0x2a00 +/** @def BT_UUID_GAP_APPEARANCE + * @brief GAP Characteristic Appearance + */ +#define BT_UUID_GAP_APPEARANCE 0x2a01 +/** @def BT_UUID_BAS_BATTERY_LEVEL + * @brief BAS Characteristic Battery Level + */ +#define BT_UUID_BAS_BATTERY_LEVEL 0x2a19 +/** @def BT_UUID_DIS_SYSTEM_ID + * @brief DIS Characteristic System ID + */ +#define BT_UUID_DIS_SYSTEM_ID 0x2a23 +/** @def BT_UUID_DIS_MODEL_NUMBER_STRING + * @brief DIS Characteristic Model Number String + */ +#define BT_UUID_DIS_MODEL_NUMBER_STRING 0x2a24 +/** @def BT_UUID_DIS_SERIAL_NUMBER_STRING + * @brief DIS Characteristic Serial Number String + */ +#define BT_UUID_DIS_SERIAL_NUMBER_STRING 0x2a25 +/** @def BT_UUID_DIS_FIRMWARE_REVISION_STRING + * @brief DIS Characteristic Firmware Revision String + */ +#define BT_UUID_DIS_FIRMWARE_REVISION_STRING 0x2a26 +/** @def BT_UUID_DIS_HARDWARE_REVISION_STRING + * @brief DIS Characteristic Hardware Revision String + */ +#define BT_UUID_DIS_HARDWARE_REVISION_STRING 0x2a27 +/** @def BT_UUID_DIS_SOFTWARE_REVISION_STRING + * @brief DIS Characteristic Software Revision String + */ +#define BT_UUID_DIS_SOFTWARE_REVISION_STRING 0x2a28 +/** @def BT_UUID_DIS_MANUFACTURER_NAME_STRING + * @brief DIS Characteristic Manufacturer Name String + */ +#define BT_UUID_DIS_MANUFACTURER_NAME_STRING 0x2a29 +/** @def BT_UUID_DIS_PNP_ID + * @brief DIS Characteristic PnP ID + */ +#define BT_UUID_DIS_PNP_ID 0x2a50 +/** @def BT_UUID_CTS_CURRENT_TIME + * @brief CTS Characteristic Current Time + */ +#define BT_UUID_CTS_CURRENT_TIME 0x2a2b +/** @def BT_UUID_HR_MEASUREMENT + * @brief HRS Characteristic Measurement Interval + */ +#define BT_UUID_HRS_MEASUREMENT 0x2a37 +/** @def BT_UUID_HRS_BODY_SENSOR + * @brief HRS Characteristic Body Sensor Location + */ +#define BT_UUID_HRS_BODY_SENSOR 0x2a38 +/** @def BT_UUID_HR_CONTROL_POINT + * @brief HRS Characteristic Control Point + */ +#define BT_UUID_HRS_CONTROL_POINT 0x2a39 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Bluetooth UUID types */ + +enum bt_uuid_type_e +{ + BT_UUID_16, + BT_UUID_128, +}; + +/* Bluetooth UUID structure */ + +struct bt_uuid_s +{ + /* UUID type */ + + uint8_t type; + union + { + /* UUID 16 bits value */ + + uint16_t u16; + + /* UUID 128 bits value */ + + uint8_t u128[16]; + } u; +}; + +/**************************************************************************** + * Public Functin Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_uuid_cmp + * + * Description: + * Compares 2 Bluetooth UUIDs, if the types are different both UUIDs are + * first converted to 128 bits format before comparing. + * + * Input Paramters: + * u1 - First Bluetooth UUID to compare + * u2 - Second Bluetooth UUID to compare + * + * Returned Value: + * negative value if < , 0 if == , else positive + * + ****************************************************************************/ + +int bt_uuid_cmp(FAR const struct bt_uuid_s *u1, + FAR const struct bt_uuid_s *u2); + +#endif /* __INCLUDE_NUTTX_WIRELESS_BT_UUID_H */ diff --git a/wireless/Kconfig b/wireless/Kconfig index 5a028e29ac8..5898b82c16e 100644 --- a/wireless/Kconfig +++ b/wireless/Kconfig @@ -11,6 +11,7 @@ config WIRELESS if WIRELESS +source wireless/bluetooth/Kconfig source wireless/ieee802154/Kconfig source wireless/pktradio/Kconfig diff --git a/wireless/Makefile b/wireless/Makefile index 352b87d1c88..2c8559454f6 100644 --- a/wireless/Makefile +++ b/wireless/Makefile @@ -50,6 +50,7 @@ VPATH = . # Add IEEE 802.15.4 files to the build +include bluetooth$(DELIM)Make.defs include ieee802154$(DELIM)Make.defs include pktradio$(DELIM)Make.defs diff --git a/wireless/bluetooth/Kconfig b/wireless/bluetooth/Kconfig new file mode 100644 index 00000000000..4190b48458a --- /dev/null +++ b/wireless/bluetooth/Kconfig @@ -0,0 +1,125 @@ +############################################################################# +# Kconfig - Bluetooth LE stack configuration options +# +# Copyright (C) 2018 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package +# where the code was released with a compatible 3-clause BSD license: +# +# Copyright (c) 2016, Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################# + +menuconfig WIRELESS_BLUETOOTH + bool + prompt "Bluetooth LE support" + default n + select MM_IOB + depends on EXPERIMENTAL + ---help--- + This option enables Bluetooth Low Energy support. + + NOTE: This selection is marked EXPERIMENTAL. It is incomplete and, + hence, untested. It still lacks any low-level Bluetooth drivers and + is missing the network interface driver. + +if WIRELESS_BLUETOOTH + +config BLUETOOTH_MAX_CONN + int + prompt "Maximum number of simultaneous connections" + default 1 + range 1 16 + ---help--- + Maximum number of simultaneous Bluetooth connections + supported. The minimum (and default) number is 1. + +config BLUETOOTH_MAX_PAIRED + int + prompt "Maximum number of paired devices" + default 1 + range 1 32 + ---help--- + Maximum number of paired Bluetooth devices. The minimum (and + default) number is 1. + +menu "Kernel Thread Configuration" + +config BLUETOOTH_RXTHREAD_STACKSIZE + int "Rx thread stack size" + default 1024 + +config BLUETOOTH_RXTHREAD_PRIORITY + int "Rx thread priority" + default 100 + range 1 255 + +config BLUETOOTH_RXTHREAD_NMSGS + int "Rx thread mqueue size" + default 16 + +config BLUETOOTH_TXCMD_STACKSIZE + int "Tx command thread stack size" + default 1024 + +config BLUETOOTH_TXCMD_PRIORITY + int "Tx command thread priority" + default 100 + range 1 255 + +config BLUETOOTH_TXCMD_NMSGS + int "Tx command thread mqueue size" + default 16 + +config BLUETOOTH_TXCONN_STACKSIZE + int "Tx connection thread stack size" + default 1024 + +config BLUETOOTH_TXCONN_PRIORITY + int "Tx connection thread priority" + default 100 + range 1 255 + +config BLUETOOTH_TXCONN_NMSGS + int "Tx connection thread mqueue size" + default 16 + +endmenu # Kernel Thread Configuration + +config BLUETOOTH_SMP_SELFTEST + bool + prompt "Bluetooth SMP self tests executed on init" + default n + ---help--- + This option enables SMP self-tests executed on startup + to verify security and crypto functions. + +endif # WIRELESS_BLUETOOTH diff --git a/wireless/bluetooth/Make.defs b/wireless/bluetooth/Make.defs new file mode 100644 index 00000000000..9494264d7d7 --- /dev/null +++ b/wireless/bluetooth/Make.defs @@ -0,0 +1,47 @@ +############################################################################ +# wireless/bluetooth/Make.defs +# +# Copyright (C) 2018 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_WIRELESS_BLUETOOTH),y) + +# Include Bluetooth support + +CSRCS += bt_atomic.c bt_att.c bt_buf.c bt_conn.c bt_gatt.c bt_hcicore.c +CSRCS += bt_keys.c bt_l2cap.c bt_queue.c bt_smp.c bt_uuid.c + +DEPPATH += --dep-path bluetooth +VPATH += :bluetooth +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)wireless$(DELIM)bluetooth} + +endif # CONFIG_WIRELESS_BLUETOOTH diff --git a/wireless/bluetooth/bt_atomic.c b/wireless/bluetooth/bt_atomic.c new file mode 100644 index 00000000000..f2f4ef313e4 --- /dev/null +++ b/wireless/bluetooth/bt_atomic.c @@ -0,0 +1,147 @@ +/**************************************************************************** + * wireless/bluetooth/bt_atomic.c + * Linux like atomic operations + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "bt_atomic.h" + +/**************************************************************************** + * Public Function + ****************************************************************************/ + +#ifndef CONFIG_HAVE_INLINE +void bt_atomic_set(FAR bt_atomic_t *ptr, bt_atomic_t value) +{ + *ptr = value; +} +#endif + +bt_atomic_t bt_atomic_incr(FAR bt_atomic_t *ptr) +{ + irqstate_t flags; + bt_atomic_t value; + + flags = spin_lock_irqsave(); + value = *ptr; + *ptr = value + 1; + spin_unlock_irqrestore(flags); + + return value; +} + +bt_atomic_t bt_atomic_decr(FAR bt_atomic_t *ptr) +{ + irqstate_t flags; + bt_atomic_t value; + + flags = spin_lock_irqsave(); + value = *ptr; + *ptr = value - 1; + spin_unlock_irqrestore(flags); + + return value; +} + +bt_atomic_t bt_atomic_setbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno) +{ + irqstate_t flags; + bt_atomic_t value; + + flags = spin_lock_irqsave(); + value = *ptr; + *ptr = value | (1 << bitno); + spin_unlock_irqrestore(flags); + + return value; +} + +bt_atomic_t bt_atomic_clrbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno) +{ + irqstate_t flags; + bt_atomic_t value; + + flags = spin_lock_irqsave(); + value = *ptr; + *ptr = value & ~(1 << bitno); + spin_unlock_irqrestore(flags); + + return value; +} + +#ifndef CONFIG_HAVE_INLINE +bt_atomic_t bt_atomic_get(FAR bt_atomic_t *ptr) +{ + return *ptr; +} +#endif + +#ifndef CONFIG_HAVE_INLINE +bool bt_atomic_testbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno) +{ + return (*ptr & (1 << bitno)) != 0; +} +#endif + +bool bt_atomic_testsetbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno) +{ + irqstate_t flags; + bt_atomic_t value; + + flags = spin_lock_irqsave(); + value = *ptr; + *ptr = value | (1 << bitno); + spin_unlock_irqrestore(flags); + + return (value & (1 << bitno)) != 0; +} + +bool bt_atomic_testclrbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno) +{ + irqstate_t flags; + bt_atomic_t value; + + flags = spin_lock_irqsave(); + value = *ptr; + *ptr = value & ~(1 << bitno); + spin_unlock_irqrestore(flags); + + return (value & (1 << bitno)) != 0; +} diff --git a/wireless/bluetooth/bt_atomic.h b/wireless/bluetooth/bt_atomic.h new file mode 100644 index 00000000000..7afc1dbed1a --- /dev/null +++ b/wireless/bluetooth/bt_atomic.h @@ -0,0 +1,101 @@ +/**************************************************************************** + * wireless/bluetooth/bt_atomic.h + * Linux like atomic operations + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __WIRELESS_BLUETOOTH_BT_ATOMIC_H +#define __WIRELESS_BLUETOOTH_BT_ATOMIC_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef uint8_t bt_atomic_t; + +/**************************************************************************** + * Inline Prototypes + ****************************************************************************/ + +#ifdef CONFIG_HAVE_INLINE +/* These operations are inherently atomic */ + +static inline void bt_atomic_set(FAR bt_atomic_t *ptr, bt_atomic_t value) +{ + *ptr = value; +} + +static inline bt_atomic_t bt_atomic_get(FAR bt_atomic_t *ptr) +{ + return *ptr; +} + +static inline bool bt_atomic_testbit(FAR bt_atomic_t *ptr, + bt_atomic_t bitno) +{ + return (*ptr & (1 << bitno)) != 0; +} +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef CONFIG_HAVE_INLINE +void bt_atomic_set(FAR bt_atomic_t *ptr, bt_atomic_t value); +bt_atomic_t bt_atomic_get(FAR bt_atomic_t *ptr); +#endif + +bt_atomic_t bt_atomic_incr(FAR bt_atomic_t *ptr); +bt_atomic_t bt_atomic_decr(FAR bt_atomic_t *ptr); +bt_atomic_t bt_atomic_setbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno); +bt_atomic_t bt_atomic_clrbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno); + +#ifndef CONFIG_HAVE_INLINE +bool bt_atomic_testbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno); +#endif + +bool bt_atomic_testsetbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno); +bool bt_atomic_testclrbit(FAR bt_atomic_t *ptr, bt_atomic_t bitno); + +#endif /* __WIRELESS_BLUETOOTH_BT_ATOMIC_H */ \ No newline at end of file diff --git a/wireless/bluetooth/bt_att.c b/wireless/bluetooth/bt_att.c new file mode 100644 index 00000000000..786dd597a4e --- /dev/null +++ b/wireless/bluetooth/bt_att.c @@ -0,0 +1,1907 @@ +/**************************************************************************** + * wireless/bluetooth/bt_att.c + * Attribute protocol handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "bt_hcicore.h" +#include "bt_conn.h" +#include "bt_l2cap.h" +#include "bt_att.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if !defined(CONFIG_BLUETOOTH_DEBUG_ATT) +# undef wlinfo +# define wlinfo(fmt, ...) +#endif + +#define BT_GATT_PERM_READ_MASK (BT_GATT_PERM_READ | \ + BT_GATT_PERM_READ_ENCRYPT | \ + BT_GATT_PERM_READ_AUTHEN | \ + BT_GATT_PERM_AUTHOR) +#define BT_GATT_PERM_WRITE_MASK (BT_GATT_PERM_WRITE | \ + BT_GATT_PERM_WRITE_ENCRYPT | \ + BT_GATT_PERM_WRITE_AUTHEN | \ + BT_GATT_PERM_AUTHOR) +#define BT_GATT_PERM_ENCRYPT_MASK (BT_GATT_PERM_READ_ENCRYPT | \ + BT_GATT_PERM_WRITE_ENCRYPT) +#define BT_GATT_PERM_AUTHEN_MASK (BT_GATT_PERM_READ_AUTHEN | \ + BT_GATT_PERM_WRITE_AUTHEN) +#define BT_ATT_OP_CMD_MASK (BT_ATT_OP_WRITE_CMD & \ + BT_ATT_OP_SIGNED_WRITE_CMD) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* ATT request context */ + +struct bt_att_req_s +{ + bt_att_func_t func; + FAR void *user_data; + bt_att_destroy_t destroy; + uint8_t op; +}; + +/* ATT channel specific context */ + +struct bt_att_s +{ + /* The connection this context is associated with */ + + FAR struct bt_conn_s *conn; + uint16_t mtu; + struct bt_att_req_s req; + + /* TODO: Allow more than one pending request */ +}; + +struct find_info_data_s +{ + FAR struct bt_conn_s *conn; + FAR struct bt_buf_s *buf; + FAR struct bt_att_find_info_rsp_s *rsp; + union + { + FAR struct bt_att_info_16_s *info16; + FAR struct bt_att_info_128_s *info128; + } u; +}; + +struct find_type_data_s +{ + FAR struct bt_conn_s *conn; + FAR struct bt_buf_s *buf; + FAR struct bt_att_handle_group_s *group; + FAR const void *value; + uint8_t value_len; +}; + +struct read_type_data_s +{ + FAR struct bt_conn_s *conn; + FAR struct bt_uuid_s *uuid; + FAR struct bt_buf_s *buf; + FAR struct bt_att_read_type_rsp_s *rsp; + FAR struct bt_att_data_s *item; +}; + +struct read_data_s +{ + FAR struct bt_conn_s *conn; + uint16_t offset; + FAR struct bt_buf_s *buf; + FAR struct bt_att_read_rsp_s *rsp; + uint8_t err; +}; + +struct read_group_data_s +{ + FAR struct bt_conn_s *conn; + FAR struct bt_uuid_s *uuid; + FAR struct bt_buf_s *buf; + FAR struct bt_att_read_group_rsp_s *rsp; + FAR struct bt_att_group_data_s *group; +}; + +struct write_data_s +{ + FAR struct bt_conn_s *conn; + FAR struct bt_buf_s *buf; + uint8_t op; + FAR const void *value; + uint8_t len; + uint16_t offset; + uint8_t err; +}; + +struct flush_data_s +{ + FAR struct bt_conn_s *conn; + FAR struct bt_buf_s *buf; + uint8_t flags; + uint8_t err; +}; + +struct handler_info_s +{ + uint8_t op; + CODE uint8_t(*func)(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf); + uint8_t expect_len; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void att_req_destroy(FAR struct bt_att_req_s *req); +static void send_err_rsp(struct bt_conn_s *conn, uint8_t req, + uint16_t handle, uint8_t err); +static uint8_t att_mtu_req(struct bt_conn_s *conn, struct bt_buf_s *data); +static uint8_t att_handle_rsp(struct bt_conn_s *conn, void *pdu, + uint16_t len, uint8_t err); +static uint8_t att_mtu_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static bool range_is_valid(uint16_t start, uint16_t end, + FAR uint16_t *err); +static uint8_t find_info_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); +static uint8_t att_find_info_rsp(FAR struct bt_conn_s *conn, + uint16_t start_handle, uint16_t end_handle); +static uint8_t att_find_info_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t find_type_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); +static uint8_t att_find_type_rsp(FAR struct bt_conn_s *conn, + FAR uint16_t start_handle, uint16_t end_handle, + FAR const void *value, uint8_t value_len); +static uint8_t att_find_type_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static bool uuid_create(FAR struct bt_uuid_s *uuid, + FAR struct bt_buf_s *data); +static uint8_t read_type_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); +static uint8_t att_read_type_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_uuid_s *uuid, uint16_t start_handle, + uint16_t end_handle); +static uint8_t att_read_type_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t err_to_att(int err); +static uint8_t check_perm(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, uint8_t mask); +static uint8_t read_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); +static uint8_t att_read_rsp(FAR struct bt_conn_s *conn, uint8_t op, + uint8_t rsp, uint16_t handle, uint16_t offset); +static uint8_t att_read_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t att_read_blob_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t att_read_mult_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t read_group_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); +static uint8_t att_read_group_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_uuid_s *uuid, uint16_t start_handle, + uint16_t end_handle); +static uint8_t att_read_group_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t write_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); +static uint8_t att_write_rsp(FAR struct bt_conn_s *conn, uint8_t op, + uint8_t rsp, uint16_t handle, uint16_t offset, + FAR const void *value, uint8_t len); +static uint8_t att_write_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t att_prepare_write_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t flush_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); +static uint8_t att_exec_write_rsp(FAR struct bt_conn_s *conn, + uint8_t flags); +static uint8_t att_exec_write_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t att_write_cmd(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t att_signed_write_cmd(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t att_error_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data); +static uint8_t att_handle_find_info_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t att_handle_find_type_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t att_handle_read_type_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t att_handle_read_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t att_handle_read_blob_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t att_handle_read_mult_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t att_handle_write_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static void bt_att_recv(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static void bt_att_connected(FAR struct bt_conn_s *conn); +static void bt_att_disconnected(FAR struct bt_conn_s *conn); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct bt_att_s g_bt_att_pool[CONFIG_BLUETOOTH_MAX_CONN]; + +static const struct bt_uuid_s g_primary_uuid = +{ + BT_UUID_16, + { + BT_UUID_GATT_PRIMARY + } +}; + +static const struct bt_uuid_s g_secondary_uuid = +{ + BT_UUID_16, + { + BT_UUID_GATT_SECONDARY + }, +}; + +static const struct handler_info_s g_handlers[] = +{ + { + BT_ATT_OP_ERROR_RSP, + att_error_rsp, + sizeof(struct bt_att_error_rsp_s) + }, + { + BT_ATT_OP_MTU_REQ, + att_mtu_req, + sizeof(struct bt_att_exchange_mtu_req_s) + }, + { + BT_ATT_OP_MTU_RSP, + att_mtu_rsp, + sizeof(struct bt_att_exchange_mtu_rsp_s) + }, + { + BT_ATT_OP_FIND_INFO_REQ, + att_find_info_req, + sizeof(struct bt_att_find_info_req_s) + }, + { + BT_ATT_OP_FIND_INFO_RSP, + att_handle_find_info_rsp, + sizeof(struct bt_att_find_info_rsp_s) + }, + { + BT_ATT_OP_FIND_TYPE_REQ, + att_find_type_req, + sizeof(struct bt_att_find_type_req_s) + }, + { + BT_ATT_OP_FIND_TYPE_RSP, + att_handle_find_type_rsp, + sizeof(struct bt_att_find_type_rsp_s) + }, + { + BT_ATT_OP_READ_TYPE_REQ, + att_read_type_req, + sizeof(struct bt_att_read_type_req_s) + }, + { + BT_ATT_OP_READ_TYPE_RSP, + att_handle_read_type_rsp, + sizeof(struct bt_att_read_type_rsp_s) + }, + { + BT_ATT_OP_READ_REQ, + att_read_req, + sizeof(struct bt_att_read_req_s) + }, + { + BT_ATT_OP_READ_RSP, + att_handle_read_rsp, + sizeof(struct bt_att_read_rsp_s) + }, + { + BT_ATT_OP_READ_BLOB_REQ, + att_read_blob_req, + sizeof(struct bt_att_read_blob_req_s) + }, + { + BT_ATT_OP_READ_BLOB_RSP, + att_handle_read_blob_rsp, + sizeof(struct bt_att_read_blob_rsp_s) + }, + { + BT_ATT_OP_READ_MULT_REQ, + att_read_mult_req, + BT_ATT_READ_MULT_MIN_LEN_REQ + }, + { + BT_ATT_OP_READ_MULT_RSP, + att_handle_read_mult_rsp, + sizeof(struct bt_att_read_mult_rsp_s) + }, + { + BT_ATT_OP_READ_GROUP_REQ, + att_read_group_req, + sizeof(struct bt_att_read_group_req_s) + }, + { + BT_ATT_OP_WRITE_REQ, + att_write_req, + sizeof(struct bt_att_write_req_s) + }, + { + BT_ATT_OP_WRITE_RSP, + att_handle_write_rsp, + 0 + }, + { + BT_ATT_OP_PREPARE_WRITE_REQ, + att_prepare_write_req, + sizeof(struct bt_att_prepare_write_req_s) + }, + { + BT_ATT_OP_EXEC_WRITE_REQ, + att_exec_write_req, + sizeof(struct bt_att_exec_write_req_s) + }, + { + BT_ATT_OP_WRITE_CMD, + att_write_cmd, + sizeof(struct bt_att_write_cmd_s) + }, + { + BT_ATT_OP_SIGNED_WRITE_CMD, + att_signed_write_cmd, + sizeof(struct bt_att_write_cmd_s) + sizeof(struct bt_att_signature_s) + } +}; + +#define NHANDLERS (sizeof(g_handlers) / sizeof(struct handler_info_s)) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void att_req_destroy(FAR struct bt_att_req_s *req) +{ + if (req->destroy) + { + req->destroy(req->user_data); + } + + memset(req, 0, sizeof(*req)); +} + +static void send_err_rsp(struct bt_conn_s *conn, uint8_t req, uint16_t handle, + uint8_t err) +{ + struct bt_att_error_rsp_s *rsp; + struct bt_buf_s *buf; + + /* Ignore opcode 0x00 */ + if (!req) + { + return; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_ERROR_RSP, sizeof(*rsp)); + if (!buf) + { + return; + } + + rsp = bt_buf_add(buf, sizeof(*rsp)); + rsp->request = req; + rsp->handle = BT_HOST2LE16(handle); + rsp->error = err; + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf); +} + +static uint8_t att_mtu_req(struct bt_conn_s *conn, struct bt_buf_s *data) +{ + FAR struct bt_att_s *att = conn->att; + struct bt_att_exchange_mtu_req_s *req; + struct bt_att_exchange_mtu_rsp_s *rsp; + struct bt_buf_s *buf; + uint16_t maxmtu; + uint16_t mtu; + + req = (void *)data->data; + + mtu = BT_LE162HOST(req->mtu); + + wlinfo("Client MTU %u\n", mtu); + + if (mtu > BT_ATT_MAX_LE_MTU || mtu < BT_ATT_DEFAULT_LE_MTU) + { + return BT_ATT_ERR_INVALID_PDU; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_MTU_RSP, sizeof(*rsp)); + if (!buf) + { + return BT_ATT_ERR_UNLIKELY; + } + + /* Select MTU based on the amount of room we have in bt_buf_s including one + * extra byte for ATT header. + */ + + maxmtu = bt_buf_tailroom(buf) + 1; + if (mtu > maxmtu) + { + mtu = maxmtu; + } + + wlinfo("Server MTU %u\n", mtu); + + att->mtu = mtu; + + rsp = bt_buf_add(buf, sizeof(*rsp)); + rsp->mtu = BT_HOST2LE16(mtu); + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf); + + return 0; +} + +static uint8_t att_handle_rsp(struct bt_conn_s *conn, void *pdu, uint16_t len, + uint8_t err) +{ + FAR struct bt_att_s *att = conn->att; + struct bt_att_req_s req; + + if (!att->req.func) + { + return 0; + } + + /* Reset request before callback so another request can be queued */ + + memcpy(&req, &att->req, sizeof(req)); + att->req.func = NULL; + + req.func(conn, err, pdu, len, req.user_data); + + att_req_destroy(&req); + return 0; +} + +static uint8_t att_mtu_rsp(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf) +{ + FAR struct bt_att_s *att = conn->att; + FAR struct bt_att_exchange_mtu_rsp_s *rsp; + uint16_t maxmtu; + uint16_t mtu; + + if (!att) + { + return 0; + } + + rsp = (void *)buf->data; + mtu = BT_LE162HOST(rsp->mtu); + + wlinfo("Server MTU %u\n", mtu); + + /* Check if MTU is within allowed range */ + + if (mtu > BT_ATT_MAX_LE_MTU || mtu < BT_ATT_DEFAULT_LE_MTU) + { + return att_handle_rsp(conn, NULL, 0, BT_ATT_ERR_INVALID_PDU); + } + + /* Clip MTU based on the maximum amount of data bt_buf_s can hold excluding + * L2CAP, ACL and driver headers. + */ + + maxmtu = BT_BUF_MAX_DATA - (sizeof(struct bt_l2cap_hdr_s) + + sizeof(struct bt_hci_acl_hdr_s) + + g_btdev.dev->head_reserve); + if (mtu > maxmtu) + { + mtu = maxmtu; + } + + att->mtu = mtu; + return att_handle_rsp(conn, rsp, buf->len, 0); +} + +static bool range_is_valid(uint16_t start, uint16_t end, FAR uint16_t *err) +{ + /* Handle 0 is invalid */ + + if (!start || !end) + { + if (err) + { + *err = 0; + } + + return false; + } + + /* Check if range is valid */ + + if (start > end) + { + if (err) + { + *err = start; + } + + return false; + } + + return true; +} + +static uint8_t find_info_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct find_info_data_s *data = user_data; + FAR struct bt_att_s *att = data->conn->att; + + wlinfo("handle 0x%04x\n", attr->handle); + + /* Initialize rsp at first entry */ + + if (!data->rsp) + { + data->rsp = bt_buf_add(data->buf, sizeof(*data->rsp)); + data->rsp->format = (attr->uuid->type == BT_UUID_16) ? + BT_ATT_INFO_16 : BT_ATT_INFO_128; + } + + switch (data->rsp->format) + { + case BT_ATT_INFO_16: + if (attr->uuid->type != BT_UUID_16) + { + return BT_GATT_ITER_STOP; + } + + /* Fast foward to next item position */ + + data->u.info16 = bt_buf_add(data->buf, sizeof(*data->u.info16)); + data->u.info16->handle = BT_HOST2LE16(attr->handle); + data->u.info16->uuid = BT_HOST2LE16(attr->uuid->u.u16); + + return att->mtu - data->buf->len > sizeof(*data->u.info16) ? + BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP; + + case BT_ATT_INFO_128: + if (attr->uuid->type != BT_UUID_128) + { + return BT_GATT_ITER_STOP; + } + + /* Fast foward to next item position */ + + data->u.info128 = bt_buf_add(data->buf, sizeof(*data->u.info128)); + data->u.info128->handle = BT_HOST2LE16(attr->handle); + memcpy(data->u.info128->uuid, attr->uuid->u.u128, + sizeof(data->u.info128->uuid)); + + return att->mtu - data->buf->len > sizeof(*data->u.info128) ? + BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP; + } + + return BT_GATT_ITER_STOP; +} + +static uint8_t att_find_info_rsp(FAR struct bt_conn_s *conn, + uint16_t start_handle, + uint16_t end_handle) +{ + struct find_info_data_s data; + + memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_INFO_RSP, 0); + if (!data.buf) + { + return BT_ATT_ERR_UNLIKELY; + } + + bt_gatt_foreach_attr(start_handle, end_handle, find_info_cb, &data); + + if (!data.rsp) + { + bt_buf_put(data.buf); + + /* Respond since handle is set */ + + send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, start_handle, + BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); + return 0; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); + return 0; +} + +static uint8_t att_find_info_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_find_info_req_s *req; + uint16_t start_handle; + uint16_t end_handle; + uint16_t err_handle; + + req = (void *)data->data; + + start_handle = BT_LE162HOST(req->start_handle); + end_handle = BT_LE162HOST(req->end_handle); + + wlinfo("start_handle 0x%04x end_handle 0x%04x\n", + start_handle, end_handle); + + if (!range_is_valid(start_handle, end_handle, &err_handle)) + { + send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, err_handle, + BT_ATT_ERR_INVALID_HANDLE); + return 0; + } + + return att_find_info_rsp(conn, start_handle, end_handle); +} + +static uint8_t find_type_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct find_type_data_s *data = user_data; + FAR struct bt_att_s *att = data->conn->att; + uint8_t uuid[16]; + int read; + + /* Skip if not a primary service */ + + if (bt_uuid_cmp(attr->uuid, &g_primary_uuid)) + { + if (data->group && attr->handle > data->group->end_handle) + { + data->group->end_handle = BT_HOST2LE16(attr->handle); + } + + return BT_GATT_ITER_CONTINUE; + } + + wlinfo("handle 0x%04x\n", attr->handle); + + /* Stop if there is no space left */ + + if (att->mtu - data->buf->len < sizeof(*data->group)) + { + return BT_GATT_ITER_STOP; + } + + /* Read attribute value and store in the buffer */ + + read = attr->read(data->conn, attr, uuid, sizeof(uuid), 0); + if (read < 0) + { + /* TODO: Return an error if this fails */ + + return BT_GATT_ITER_STOP; + } + + /* Check if data matches */ + + if (read != data->value_len || memcmp(data->value, uuid, read)) + { + /* If a group exists stop otherwise continue */ + + return data->group ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE; + } + + /* Fast foward to next item position */ + + data->group = bt_buf_add(data->buf, sizeof(*data->group)); + data->group->start_handle = BT_HOST2LE16(attr->handle); + data->group->end_handle = BT_HOST2LE16(attr->handle); + + /* Continue to find the end_handle */ + + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t att_find_type_rsp(FAR struct bt_conn_s *conn, + FAR uint16_t start_handle, + uint16_t end_handle, + FAR const void *value, + uint8_t value_len) +{ + struct find_type_data_s data; + + memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_TYPE_RSP, 0); + if (!data.buf) + { + return BT_ATT_ERR_UNLIKELY; + } + + data.value = value; + data.value_len = value_len; + + bt_gatt_foreach_attr(start_handle, end_handle, find_type_cb, &data); + + if (!data.group) + { + bt_buf_put(data.buf); + + /* Respond since handle is set */ + + send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, start_handle, + BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); + return 0; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); + return 0; +} + +static uint8_t att_find_type_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_find_type_req_s *req; + FAR uint8_t *value; + uint16_t start_handle; + uint16_t end_handle; + uint16_t err_handle; + uint16_t type; + + req = (FAR void *)data->data; + + start_handle = BT_LE162HOST(req->start_handle); + end_handle = BT_LE162HOST(req->end_handle); + type = BT_LE162HOST(req->type); + value = bt_buf_pull(data, sizeof(*req)); + + wlinfo("start_handle 0x%04x end_handle 0x%04x type %u\n", start_handle, + end_handle, type); + + if (!range_is_valid(start_handle, end_handle, &err_handle)) + { + send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, err_handle, + BT_ATT_ERR_INVALID_HANDLE); + return 0; + } + + /* The Attribute Protocol Find By Type Value Request shall be used with the + * Attribute Type parameter set to the UUID for «Primary Service» and the + * Attribute Value set to the 16-bit Bluetooth UUID or 128-bit UUID for the + * specific primary service. + */ + + if (type != BT_UUID_GATT_PRIMARY) + { + send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, start_handle, + BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); + return 0; + } + + return att_find_type_rsp(conn, start_handle, end_handle, value, data->len); +} + +static bool uuid_create(FAR struct bt_uuid_s *uuid, FAR struct bt_buf_s *data) +{ + if (data->len > sizeof(uuid->u.u128)) + { + return false; + } + + switch (data->len) + { + case 2: + uuid->type = BT_UUID_16; + uuid->u.u16 = bt_buf_pull_le16(data); + return true; + + case 16: + uuid->type = BT_UUID_128; + memcpy(uuid->u.u128, data->data, data->len); + return true; + } + + return false; +} + +static uint8_t read_type_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct read_type_data_s *data = user_data; + FAR struct bt_att_s *att = data->conn->att; + int read; + + /* Skip if doesn't match */ + + if (bt_uuid_cmp(attr->uuid, data->uuid)) + { + return BT_GATT_ITER_CONTINUE; + } + + wlinfo("handle 0x%04x\n", attr->handle); + + /* Fast foward to next item position */ + + data->item = bt_buf_add(data->buf, sizeof(*data->item)); + data->item->handle = BT_HOST2LE16(attr->handle); + + /* Read attribute value and store in the buffer */ + + read = attr->read(data->conn, attr, data->buf->data + data->buf->len, + att->mtu - data->buf->len, 0); + if (read < 0) + { + /* TODO: Handle read errors */ + + return BT_GATT_ITER_STOP; + } + + if (!data->rsp->len) + { + /* Set len to be the first item found */ + + data->rsp->len = read + sizeof(*data->item); + } + else if (data->rsp->len != read + sizeof(*data->item)) + { + /* All items should have the same size */ + + data->buf->len -= sizeof(*data->item); + return BT_GATT_ITER_STOP; + } + + bt_buf_add(data->buf, read); + + /* Return true only if there are still space for more items */ + + return att->mtu - data->buf->len > data->rsp->len ? + BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP; +} + +static uint8_t att_read_type_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_uuid_s *uuid, + uint16_t start_handle, + uint16_t end_handle) +{ + struct read_type_data_s data; + + memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_RSP, + sizeof(*data.rsp)); + if (!data.buf) + { + return BT_ATT_ERR_UNLIKELY; + } + + data.uuid = uuid; + data.rsp = bt_buf_add(data.buf, sizeof(*data.rsp)); + data.rsp->len = 0; + + bt_gatt_foreach_attr(start_handle, end_handle, read_type_cb, &data); + + if (!data.rsp->len) + { + bt_buf_put(data.buf); + + /* Response here since handle is set */ + + send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, start_handle, + BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); + return 0; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); + return 0; +} + +static uint8_t att_read_type_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_read_type_req_s *req; + struct bt_uuid_s uuid; + uint16_t start_handle; + uint16_t end_handle; + uint16_t err_handle; + + /* Type can only be UUID16 or UUID128 */ + + if (data->len != sizeof(*req) + sizeof(uuid.u.u16) && + data->len != sizeof(*req) + sizeof(uuid.u.u128)) + { + return BT_ATT_ERR_INVALID_PDU; + } + + req = (FAR void *)data->data; + start_handle = BT_LE162HOST(req->start_handle); + end_handle = BT_LE162HOST(req->end_handle); + bt_buf_pull(data, sizeof(*req)); + + if (!uuid_create(&uuid, data)) + { + return BT_ATT_ERR_UNLIKELY; + } + + wlinfo("start_handle 0x%04x end_handle 0x%04x type %u\n", + start_handle, end_handle, uuid.u16); + + if (!range_is_valid(start_handle, end_handle, &err_handle)) + { + send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, err_handle, + BT_ATT_ERR_INVALID_HANDLE); + return 0; + } + + return att_read_type_rsp(conn, &uuid, start_handle, end_handle); +} + +static uint8_t err_to_att(int err) +{ + wlinfo("%d", err); + + switch (err) + { + case -EINVAL: + return BT_ATT_ERR_INVALID_OFFSET; + + case -EFBIG: + return BT_ATT_ERR_INVALID_ATTRIBUTE_LEN; + + default: + return BT_ATT_ERR_UNLIKELY; + } +} + +static uint8_t check_perm(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + uint8_t mask) +{ + if ((mask & BT_GATT_PERM_READ) && !(attr->perm & BT_GATT_PERM_READ)) + { + return BT_ATT_ERR_READ_NOT_PERMITTED; + } + + if ((mask & BT_GATT_PERM_WRITE) && !(attr->perm & BT_GATT_PERM_WRITE)) + { + return BT_ATT_ERR_READ_NOT_PERMITTED; + } + + mask &= attr->perm; + if (mask & BT_GATT_PERM_AUTHEN_MASK) + { + /* TODO: Check conn authentication */ + + return BT_ATT_ERR_AUTHENTICATION; + } + + if ((mask & BT_GATT_PERM_ENCRYPT_MASK) && !conn->encrypt) + { + return BT_ATT_ERR_INSUFFICIENT_ENCRYPTION; + } + + if (mask & BT_GATT_PERM_AUTHOR) + { + return BT_ATT_ERR_AUTHORIZATION; + } + + return 0; +} + +static uint8_t read_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct read_data_s *data = user_data; + FAR struct bt_att_s *att = data->conn->att; + int read; + + wlinfo("handle 0x%04x\n", attr->handle); + + data->rsp = bt_buf_add(data->buf, sizeof(*data->rsp)); + + if (!attr->read) + { + data->err = BT_ATT_ERR_READ_NOT_PERMITTED; + return BT_GATT_ITER_STOP; + } + + /* Check attribute permissions */ + + data->err = check_perm(data->conn, attr, BT_GATT_PERM_READ_MASK); + if (data->err) + { + return BT_GATT_ITER_STOP; + } + + /* Read attribute value and store in the buffer */ + + read = attr->read(data->conn, attr, data->buf->data + data->buf->len, + att->mtu - data->buf->len, data->offset); + if (read < 0) + { + data->err = err_to_att(read); + return BT_GATT_ITER_STOP; + } + + bt_buf_add(data->buf, read); + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t att_read_rsp(FAR struct bt_conn_s *conn, uint8_t op, + uint8_t rsp, uint16_t handle, uint16_t offset) +{ + struct read_data_s data; + + if (!handle) + { + return BT_ATT_ERR_INVALID_HANDLE; + } + + memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, rsp, 0); + if (!data.buf) + { + return BT_ATT_ERR_UNLIKELY; + } + + data.conn = conn; + data.offset = offset; + + bt_gatt_foreach_attr(handle, handle, read_cb, &data); + + /* In case of error discard data and respond with an error */ + + if (data.err) + { + bt_buf_put(data.buf); + + /* Respond here since handle is set */ + + send_err_rsp(conn, op, handle, data.err); + return 0; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); + return 0; +} + +static uint8_t att_read_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_read_req_s *req; + uint16_t handle; + + req = (void *)data->data; + + handle = BT_LE162HOST(req->handle); + + wlinfo("handle 0x%04x\n", handle); + + return att_read_rsp(conn, BT_ATT_OP_READ_REQ, BT_ATT_OP_READ_RSP, handle, 0); +} + +static uint8_t att_read_blob_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_read_blob_req_s *req; + uint16_t handle, offset; + + req = (FAR void *)data->data; + + handle = BT_LE162HOST(req->handle); + offset = BT_LE162HOST(req->offset); + + wlinfo("handle 0x%04x offset %u\n", handle, offset); + + return att_read_rsp(conn, BT_ATT_OP_READ_BLOB_REQ, + BT_ATT_OP_READ_BLOB_RSP, handle, offset); +} + +static uint8_t att_read_mult_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + struct read_data_s data; + uint16_t handle; + + memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_RSP, 0); + if (!data.buf) + { + return BT_ATT_ERR_UNLIKELY; + } + + data.conn = conn; + + while (buf->len >= sizeof(uint16_t)) + { + handle = bt_buf_pull_le16(buf); + + wlinfo("handle 0x%04x \n", handle); + + bt_gatt_foreach_attr(handle, handle, read_cb, &data); + + /* Stop reading in case of error */ + + if (data.err) + { + bt_buf_put(data.buf); + + /* Respond here since handle is set */ + + send_err_rsp(conn, BT_ATT_OP_READ_MULT_REQ, handle, data.err); + return 0; + } + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); + + return 0; +} + +static uint8_t read_group_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct read_group_data_s *data = user_data; + FAR struct bt_att_s *att = data->conn->att; + int read; + + /* If UUID don't match update group end_handle */ + + if (bt_uuid_cmp(attr->uuid, data->uuid)) + { + if (data->group && attr->handle > data->group->end_handle) + { + data->group->end_handle = BT_HOST2LE16(attr->handle); + } + + return BT_GATT_ITER_CONTINUE; + } + + wlinfo("handle 0x%04x\n", attr->handle); + + /* Stop if there is no space left */ + + if (data->rsp->len && att->mtu - data->buf->len < data->rsp->len) + { + return BT_GATT_ITER_STOP; + } + + /* Fast forward to next group position */ + + data->group = bt_buf_add(data->buf, sizeof(*data->group)); + + /* Initialize group handle range */ + + data->group->start_handle = BT_HOST2LE16(attr->handle); + data->group->end_handle = BT_HOST2LE16(attr->handle); + + /* Read attribute value and store in the buffer */ + + read = attr->read(data->conn, attr, data->buf->data + data->buf->len, + att->mtu - data->buf->len, 0); + if (read < 0) + { + /* TODO: Handle read errors */ + + return BT_GATT_ITER_STOP; + } + + if (!data->rsp->len) + { + /* Set len to be the first group found */ + + data->rsp->len = read + sizeof(*data->group); + } + else if (data->rsp->len != read + sizeof(*data->group)) + { + /* All groups entries should have the same size */ + + data->buf->len -= sizeof(*data->group); + return false; + } + + bt_buf_add(data->buf, read); + + /* Continue to find the end handle */ + + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t att_read_group_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_uuid_s *uuid, + uint16_t start_handle, + uint16_t end_handle) +{ + struct read_group_data_s data; + + memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_GROUP_RSP, + sizeof(*data.rsp)); + if (!data.buf) + { + return BT_ATT_ERR_UNLIKELY; + } + + data.conn = conn; + data.uuid = uuid; + data.rsp = bt_buf_add(data.buf, sizeof(*data.rsp)); + data.rsp->len = 0; + + bt_gatt_foreach_attr(start_handle, end_handle, read_group_cb, &data); + + if (!data.rsp->len) + { + bt_buf_put(data.buf); + + /* Respond here since handle is set */ + + send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, start_handle, + BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); + return 0; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); + return 0; +} + +static uint8_t att_read_group_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_read_group_req_s *req; + struct bt_uuid_s uuid; + uint16_t start_handle; + uint16_t end_handle; + uint16_t err_handle; + + /* Type can only be UUID16 or UUID128 */ + + if (data->len != sizeof(*req) + sizeof(uuid.u.u16) && + data->len != sizeof(*req) + sizeof(uuid.u.u128)) + { + return BT_ATT_ERR_INVALID_PDU; + } + + req = (FAR void *)data->data; + start_handle = BT_LE162HOST(req->start_handle); + end_handle = BT_LE162HOST(req->end_handle); + bt_buf_pull(data, sizeof(*req)); + + if (!uuid_create(&uuid, data)) + { + return BT_ATT_ERR_UNLIKELY; + } + + wlinfo("start_handle 0x%04x end_handle 0x%04x type %u\n", + start_handle, end_handle, uuid.u16); + + if (!range_is_valid(start_handle, end_handle, &err_handle)) + { + send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, err_handle, + BT_ATT_ERR_INVALID_HANDLE); + return 0; + } + + /* Core v4.2, Vol 3, sec 2.5.3 Attribute Grouping: Not all of the grouping + * attributes can be used in the ATT Read By Group Type Request. The + * «Primary Service» and «Secondary Service» grouping types may be used + * in the Read By Group Type Request. The «Characteristic» grouping type + * shall not be used in the ATT Read By Group Type Request. + */ + + if (bt_uuid_cmp(&uuid, &g_primary_uuid) && bt_uuid_cmp(&uuid, &g_secondary_uuid)) + { + send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, start_handle, + BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE); + return 0; + } + + return att_read_group_rsp(conn, &uuid, start_handle, end_handle); +} + +static uint8_t write_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct write_data_s *data = user_data; + int write; + + wlinfo("handle 0x%04x\n", attr->handle); + + /* Check for write support and flush support in case of prepare */ + + if (!attr->write || (data->op == BT_ATT_OP_PREPARE_WRITE_REQ && !attr->flush)) + { + data->err = BT_ATT_ERR_WRITE_NOT_PERMITTED; + return BT_GATT_ITER_STOP; + } + + /* Check attribute permissions */ + + data->err = check_perm(data->conn, attr, BT_GATT_PERM_WRITE_MASK); + if (data->err) + { + return BT_GATT_ITER_STOP; + } + + /* Read attribute value and store in the buffer */ + + write = attr->write(data->conn, attr, data->value, data->len, data->offset); + if (write < 0 || write != data->len) + { + data->err = err_to_att(write); + return BT_GATT_ITER_STOP; + } + + /* Flush in case of regular write operation */ + + if (attr->flush && data->op != BT_ATT_OP_PREPARE_WRITE_REQ) + { + write = attr->flush(data->conn, attr, BT_GATT_FLUSH_SYNC); + if (write < 0) + { + data->err = err_to_att(write); + return BT_GATT_ITER_STOP; + } + } + + data->err = 0; + + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t att_write_rsp(FAR struct bt_conn_s *conn, uint8_t op, + uint8_t rsp, uint16_t handle, + uint16_t offset, FAR const void *value, + uint8_t len) +{ + struct write_data_s data; + + if (!handle) + { + return BT_ATT_ERR_INVALID_HANDLE; + } + + memset(&data, 0, sizeof(data)); + + /* Only allocate buf if required to respond */ + + if (rsp) + { + data.buf = bt_att_create_pdu(conn, rsp, 0); + if (!data.buf) + { + return BT_ATT_ERR_UNLIKELY; + } + } + + data.conn = conn; + data.op = op; + data.offset = offset; + data.value = value; + data.len = len; + data.err = BT_ATT_ERR_INVALID_HANDLE; + + bt_gatt_foreach_attr(handle, handle, write_cb, &data); + + /* In case of error discard data and respond with an error */ + + if (data.err) + { + if (rsp) + { + bt_buf_put(data.buf); + + /* Respond here since handle is set */ + + send_err_rsp(conn, op, handle, data.err); + } + + return 0; + } + + if (data.buf) + { + /* Add prepare write response */ + + if (rsp == BT_ATT_OP_PREPARE_WRITE_RSP) + { + FAR struct bt_att_prepare_write_rsp_s *wrrsp; + + wrrsp = bt_buf_add(data.buf, sizeof(*wrrsp)); + wrrsp->handle = BT_HOST2LE16(handle); + wrrsp->offset = BT_HOST2LE16(offset); + bt_buf_add(data.buf, len); + memcpy(wrrsp->value, value, len); + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); + } + + return 0; +} + +static uint8_t att_write_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_write_req_s *req; + uint16_t handle; + + req = (FAR void *)data->data; + + handle = BT_LE162HOST(req->handle); + bt_buf_pull(data, sizeof(*req)); + + wlinfo("handle 0x%04x\n", handle); + + return att_write_rsp(conn, BT_ATT_OP_WRITE_REQ, BT_ATT_OP_WRITE_RSP, + handle, 0, data->data, data->len); +} + +static uint8_t att_prepare_write_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_prepare_write_req_s *req; + uint16_t handle; + uint16_t offset; + + req = (FAR void *)data->data; + handle = BT_LE162HOST(req->handle); + offset = BT_LE162HOST(req->offset); + bt_buf_pull(data, sizeof(*req)); + + wlinfo("handle 0x%04x offset %u\n", handle, offset); + + return att_write_rsp(conn, BT_ATT_OP_PREPARE_WRITE_REQ, + BT_ATT_OP_PREPARE_WRITE_RSP, handle, offset, + data->data, data->len); +} +typedef CODE uint8_t + (*bt_gatt_attr_func_t)(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data); + +static uint8_t flush_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct flush_data_s *data = user_data; + int err; + + /* If attribute cannot be flushed continue to next */ + + if (!attr->flush) + { + return BT_GATT_ITER_CONTINUE; + } + + wlinfo("handle 0x%04x flags 0x%02x\n", attr->handle, data->flags); + + /* Flush attribute any data cached to be written */ + + err = attr->flush(data->conn, attr, data->flags); + if (err < 0) + { + data->err = err_to_att(err); + return BT_GATT_ITER_STOP; + } + + data->err = 0; + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t att_exec_write_rsp(FAR struct bt_conn_s *conn, uint8_t flags) +{ + struct flush_data_s data; + + memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_EXEC_WRITE_RSP, 0); + if (!data.buf) + { + return BT_ATT_ERR_UNLIKELY; + } + + data.conn = conn; + data.flags = flags; + + /* Apply to the whole database */ + + bt_gatt_foreach_attr(0x0000, 0xffff, flush_cb, &data); + + /* In case of error discard data */ + + if (data.err) + { + bt_buf_put(data.buf); + return data.err; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); + return 0; +} + +static uint8_t att_exec_write_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_exec_write_req_s *req; + + req = (FAR void *)data->data; + + wlinfo("flags 0x%02x\n", req->flags); + + return att_exec_write_rsp(conn, req->flags); +} + +static uint8_t att_write_cmd(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_write_cmd_s *req; + uint16_t handle; + + if (data->len < sizeof(*req)) + { + /* Commands don't have any response */ + + return 0; + } + + req = (FAR void *)data->data; + + handle = BT_LE162HOST(req->handle); + + wlinfo("handle 0x%04x\n", handle); + + return att_write_rsp(conn, 0, 0, handle, 0, data->data, data->len); +} + +static uint8_t att_signed_write_cmd(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_signed_write_cmd_s *req; + uint16_t handle; + + req = (FAR void *)data->data; + + handle = BT_LE162HOST(req->handle); + bt_buf_pull(data, sizeof(*req)); + + wlinfo("handle 0x%04x\n", handle); + + /* TODO: Validate signature */ + + return att_write_rsp(conn, 0, 0, handle, 0, data->data, + data->len - sizeof(struct bt_att_signature_s)); +} + +static uint8_t att_error_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *data) +{ + FAR struct bt_att_s *att = conn->att; + FAR struct bt_att_error_rsp_s *rsp; + uint8_t err; + + rsp = (FAR void *)data->data; + + wlinfo("request 0x%02x handle 0x%04x error 0x%02x\n", + rsp->request, BT_LE162HOST(rsp->handle), rsp->error); + + /* Match request with response */ + + err = rsp->request == att->req.op ? rsp->error : BT_ATT_ERR_UNLIKELY; + return att_handle_rsp(conn, NULL, 0, err); +} + +static uint8_t att_handle_find_info_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + wlinfo("\n"); + + return att_handle_rsp(conn, buf->data, buf->len, 0); +} + +static uint8_t att_handle_find_type_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + wlinfo("\n"); + + return att_handle_rsp(conn, buf->data, buf->len, 0); +} + +static uint8_t att_handle_read_type_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + wlinfo("\n"); + + return att_handle_rsp(conn, buf->data, buf->len, 0); +} + +static uint8_t att_handle_read_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + wlinfo("\n"); + + return att_handle_rsp(conn, buf->data, buf->len, 0); +} + +static uint8_t att_handle_read_blob_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + wlinfo("\n"); + + return att_handle_rsp(conn, buf->data, buf->len, 0); +} + +static uint8_t att_handle_read_mult_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + wlinfo("\n"); + + return att_handle_rsp(conn, buf->data, buf->len, 0); +} + +static uint8_t att_handle_write_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + wlinfo("\n"); + + return att_handle_rsp(conn, buf->data, buf->len, 0); +} + +static void bt_att_recv(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf) +{ + FAR struct bt_att_hdr_s *hdr = (FAR void *)buf->data; + uint8_t err = BT_ATT_ERR_NOT_SUPPORTED; + size_t i; + + DEBUGASSERT(conn->att); + + if (buf->len < sizeof(*hdr)) + { + wlerr("ERROR: Too small ATT PDU received\n"); + goto done; + } + + wlinfo("Received ATT code 0x%02x len %u\n", hdr->code, buf->len); + + bt_buf_pull(buf, sizeof(*hdr)); + + for (i = 0; i < NHANDLERS; i++) + { + if (hdr->code != g_handlers[i].op) + { + continue; + } + + if (buf->len < g_handlers[i].expect_len) + { + wlerr("ERROR: Invalid len %u for code 0x%02x\n", + buf->len, hdr->code); + err = BT_ATT_ERR_INVALID_PDU; + break; + } + + err = g_handlers[i].func(conn, buf); + break; + } + + /* Commands don't have response */ + + if ((hdr->code & BT_ATT_OP_CMD_MASK)) + { + goto done; + } + + if (err) + { + wlinfo("ATT error 0x%02x", err); + send_err_rsp(conn, hdr->code, 0, err); + } + +done: + bt_buf_put(buf); +} + +static void bt_att_connected(FAR struct bt_conn_s *conn) +{ + int i; + + wlinfo("conn %p handle %u\n", conn, conn->handle); + + for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++) + { + FAR struct bt_att_s *att = &g_bt_att_pool[i]; + + if (!att->conn) + { + att->conn = conn; + conn->att = att; + att->mtu = BT_ATT_DEFAULT_LE_MTU; + bt_gatt_connected(conn); + return; + } + } + + wlerr("ERROR: No available ATT context for conn %p\n", conn); +} + +static void bt_att_disconnected(FAR struct bt_conn_s *conn) +{ + FAR struct bt_att_s *att = conn->att; + + if (!att) + { + return; + } + + wlinfo("conn %p handle %u\n", conn, conn->handle); + + conn->att = NULL; + memset(att, 0, sizeof(*att)); + bt_gatt_disconnected(conn); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void bt_att_init(void) +{ + static struct bt_l2cap_chan_s chan = + { + .cid = BT_L2CAP_CID_ATT, + .recv = bt_att_recv, + .connected = bt_att_connected, + .disconnected = bt_att_disconnected, + }; + + bt_l2cap_chan_register(&chan); +} + +FAR struct bt_buf_s *bt_att_create_pdu(FAR struct bt_conn_s *conn, uint8_t op, + size_t len) +{ + FAR struct bt_att_hdr_s *hdr; + FAR struct bt_buf_s *buf; + FAR FAR struct bt_att_s *att = conn->att; + + if (len + sizeof(op) > att->mtu) + { + wlwarn("ATT MTU exceeded, max %u, wanted %u\n", att->mtu, len); + return NULL; + } + + buf = bt_l2cap_create_pdu(conn); + if (!buf) + { + return NULL; + } + + hdr = bt_buf_add(buf, sizeof(*hdr)); + hdr->code = op; + + return buf; +} + +int bt_att_send(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf, + bt_att_func_t func, FAR void *user_data, + bt_att_destroy_t destroy) +{ + FAR struct bt_att_s *att; + + if (!conn) + { + return -EINVAL; + } + + att = conn->att; + if (!att) + { + return -ENOTCONN; + } + + if (func) + { + FAR struct bt_att_hdr_s *hdr; + + /* Check if there is a request pending */ + + if (att->req.func) + { + /* TODO: Allow more than one pending request */ + + return -EBUSY; + } + + hdr = (void *)buf->data; + att->req.op = hdr->code; + att->req.func = func; + att->req.user_data = user_data; + att->req.destroy = destroy; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf); + return 0; +} + +void bt_att_cancel(FAR struct bt_conn_s *conn) +{ + FAR struct bt_att_s *att; + + if (!conn) + { + return; + } + + att = conn->att; + if (!att) + { + return; + } + + att_req_destroy(&att->req); +} diff --git a/wireless/bluetooth/bt_att.h b/wireless/bluetooth/bt_att.h new file mode 100644 index 00000000000..b4b150685b9 --- /dev/null +++ b/wireless/bluetooth/bt_att.h @@ -0,0 +1,451 @@ +/**************************************************************************** + * wireless/bluetooth/bt_att.h + * Attribute protocol handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __WIRELESS_BLUETOOTH_BT_ATTR_H +#define __WIRELESS_BLUETOOTH_BT_ATTR_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BT_ATT_DEFAULT_LE_MTU 23 +#define BT_ATT_MAX_LE_MTU 517 + +/* Error codes for Error response PDU */ + +#define BT_ATT_ERR_INVALID_HANDLE 0x01 +#define BT_ATT_ERR_READ_NOT_PERMITTED 0x02 +#define BT_ATT_ERR_WRITE_NOT_PERMITTED 0x03 +#define BT_ATT_ERR_INVALID_PDU 0x04 +#define BT_ATT_ERR_AUTHENTICATION 0x05 +#define BT_ATT_ERR_NOT_SUPPORTED 0x06 +#define BT_ATT_ERR_INVALID_OFFSET 0x07 +#define BT_ATT_ERR_AUTHORIZATION 0x08 +#define BT_ATT_ERR_PREPARE_QUEUE_FULL 0x09 +#define BT_ATT_ERR_ATTRIBUTE_NOT_FOUND 0x0a +#define BT_ATT_ERR_ATTRIBUTE_NOT_LONG 0x0b +#define BT_ATT_ERR_ENCRYPTION_KEY_SIZE 0x0c +#define BT_ATT_ERR_INVALID_ATTRIBUTE_LEN 0x0d +#define BT_ATT_ERR_UNLIKELY 0x0e +#define BT_ATT_ERR_INSUFFICIENT_ENCRYPTION 0x0f +#define BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE 0x10 +#define BT_ATT_ERR_INSUFFICIENT_RESOURCES 0x11 + +#define BT_ATT_OP_ERROR_RSP 0x01 +#define BT_ATT_OP_MTU_REQ 0x02 +#define BT_ATT_OP_MTU_RSP 0x03 + +/* Find Information Request */ + +#define BT_ATT_OP_FIND_INFO_REQ 0x04 + +/* Find Information Response */ + +#define BT_ATT_INFO_16 0x01 +#define BT_ATT_INFO_128 0x02 +#define BT_ATT_OP_FIND_INFO_RSP 0x05 + +/* Find By Type Value Request */ + +#define BT_ATT_OP_FIND_TYPE_REQ 0x06 + +/* Find By Type Value Response */ + +#define BT_ATT_OP_FIND_TYPE_RSP 0x07 + +/* Read By Type Request */ + +#define BT_ATT_OP_READ_TYPE_REQ 0x08 + +/* Read By Type Response */ + +#define BT_ATT_OP_READ_TYPE_RSP 0x09 + +/* Read Request */ + +#define BT_ATT_OP_READ_REQ 0x0a + +/* Read Response */ + +#define BT_ATT_OP_READ_RSP 0x0b + +/* Read Blob Request */ + +#define BT_ATT_OP_READ_BLOB_REQ 0x0c + +/* Read Blob Response */ + +#define BT_ATT_OP_READ_BLOB_RSP 0x0d + +/* Read Multiple Request */ + +#define BT_ATT_READ_MULT_MIN_LEN_REQ 0x04 +#define BT_ATT_OP_READ_MULT_REQ 0x0e + +/* Read Multiple Response */ + +#define BT_ATT_OP_READ_MULT_RSP 0x0f + +/* Read by Group Type Request */ + +#define BT_ATT_OP_READ_GROUP_REQ 0x10 + +/* Read by Group Type Response */ + +#define BT_ATT_OP_READ_GROUP_RSP 0x11 + +/* Write Request */ + +#define BT_ATT_OP_WRITE_REQ 0x12 + +/* Write Response */ + +#define BT_ATT_OP_WRITE_RSP 0x13 + +/* Prepare Write Request */ + +#define BT_ATT_OP_PREPARE_WRITE_REQ 0x16 + +/* Prepare Write Respond */ + +#define BT_ATT_OP_PREPARE_WRITE_RSP 0x17 + +/* Execute Write Request */ + +#define BT_ATT_FLAG_CANCEL 0x00 +#define BT_ATT_FLAG_EXEC 0x01 +#define BT_ATT_OP_EXEC_WRITE_REQ 0x18 + +/* Execute Write Response */ + +#define BT_ATT_OP_EXEC_WRITE_RSP 0x19 + +/* Handle Value Notification */ + +#define BT_ATT_OP_NOTIFY 0x1b + +/* Handle Value Indication */ + +#define BT_ATT_OP_INDICATE 0x1d + +/* Handle Value Confirm */ + +#define BT_ATT_OP_CONFIRM 0x1f + +/* Write Command */ + +#define BT_ATT_OP_WRITE_CMD 0x52 + +/* Signed Write Command */ + +#define BT_ATT_OP_SIGNED_WRITE_CMD 0xd2 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +begin_packed_struct struct bt_att_hdr_s +{ + uint8_t code; +} end_packed_struct; + +begin_packed_struct struct bt_att_error_rsp_s +{ + uint8_t request; + uint16_t handle; + uint8_t error; +} end_packed_struct; + +begin_packed_struct struct bt_att_exchange_mtu_req_s +{ + uint16_t mtu; +} end_packed_struct; + +begin_packed_struct struct bt_att_exchange_mtu_rsp_s +{ + uint16_t mtu; +} end_packed_struct; + +/* Find Information Request */ + +begin_packed_struct struct bt_att_find_info_req_s +{ + uint16_t start_handle; + uint16_t end_handle; +} end_packed_struct; + +begin_packed_struct struct bt_att_info_16_s +{ + uint16_t handle; + uint16_t uuid; +} end_packed_struct; + +begin_packed_struct struct bt_att_info_128_s +{ + uint16_t handle; + uint8_t uuid[16]; +} end_packed_struct; + +/* Find Information Response */ + +begin_packed_struct struct bt_att_find_info_rsp_s +{ + uint8_t format; + uint8_t info[0]; +} end_packed_struct; + +/* Find By Type Value Request */ + +begin_packed_struct struct bt_att_find_type_req_s +{ + uint16_t start_handle; + uint16_t end_handle; + uint16_t type; + uint8_t value[0]; +} end_packed_struct; + +begin_packed_struct struct bt_att_handle_group_s +{ + uint16_t start_handle; + uint16_t end_handle; +} end_packed_struct; + +/* Find By Type Value Response */ + +begin_packed_struct struct bt_att_find_type_rsp_s +{ + struct bt_att_handle_group_s list[0]; +} end_packed_struct; + +/* Read By Type Request */ + +begin_packed_struct struct bt_att_read_type_req_s +{ + uint16_t start_handle; + uint16_t end_handle; + uint8_t uuid[0]; +} end_packed_struct; + +begin_packed_struct struct bt_att_data_s +{ + uint16_t handle; + uint8_t value[0]; +} end_packed_struct; + +/* Read By Type Response */ + +begin_packed_struct struct bt_att_read_type_rsp_s +{ + uint8_t len; + struct bt_att_data_s data[0]; +} end_packed_struct; + +/* Read Request */ + +begin_packed_struct struct bt_att_read_req_s +{ + uint16_t handle; +} end_packed_struct; + +/* Read Response */ + +begin_packed_struct struct bt_att_read_rsp_s +{ + uint8_t value[0]; +} end_packed_struct; + +/* Read Blob Request */ + +begin_packed_struct struct bt_att_read_blob_req_s +{ + uint16_t handle; + uint16_t offset; +} end_packed_struct; + +/* Read Blob Response */ + +begin_packed_struct struct bt_att_read_blob_rsp_s +{ + uint8_t value[0]; +} end_packed_struct; + +/* Read Multiple Request */ + +begin_packed_struct struct bt_att_read_mult_req_s +{ + uint16_t handles[0]; +} end_packed_struct; + +/* Read Multiple Response */ + +begin_packed_struct struct bt_att_read_mult_rsp_s +{ + uint8_t value[0]; +} end_packed_struct; + +/* Read by Group Type Request */ + +struct bt_att_read_group_req_s +{ + uint16_t start_handle; + uint16_t end_handle; + uint8_t uuid[0]; +} end_packed_struct; + +begin_packed_struct struct bt_att_group_data_s +{ + uint16_t start_handle; + uint16_t end_handle; + uint8_t value[0]; +} end_packed_struct; + +/* Read by Group Type Response */ + +begin_packed_struct struct bt_att_read_group_rsp_s +{ + uint8_t len; + struct bt_att_group_data_s data[0]; +} end_packed_struct; + +/* Write Request */ + +begin_packed_struct struct bt_att_write_req_s +{ + uint16_t handle; + uint8_t value[0]; +} end_packed_struct; + +/* Write Response */ + +/* Prepare Write Request */ + +begin_packed_struct struct bt_att_prepare_write_req_s +{ + uint16_t handle; + uint16_t offset; + uint8_t value[0]; +} end_packed_struct; + +/* Prepare Write Response */ + +begin_packed_struct struct bt_att_prepare_write_rsp_s +{ + uint16_t handle; + uint16_t offset; + uint8_t value[0]; +} end_packed_struct; + +/* Execute Write Request */ + +begin_packed_struct struct bt_att_exec_write_req_s +{ + uint8_t flags; +} end_packed_struct; + +/* Execute Write Response */ + +/* Handle Value Notification */ + +begin_packed_struct struct bt_att_notify_s +{ + uint16_t handle; + uint8_t value[0]; +} end_packed_struct; + +/* Handle Value Indication */ + +begin_packed_struct struct bt_att_indicate_s +{ + uint16_t handle; + uint8_t value[0]; +} end_packed_struct; + +/* Handle Value Confirm */ + +begin_packed_struct struct bt_att_signature_s +{ + uint8_t value[12]; +} end_packed_struct; + +/* Write Command */ + +begin_packed_struct struct bt_att_write_cmd_s +{ + uint16_t handle; + uint8_t value[0]; +} end_packed_struct; + +/* Signed Write Command */ + +begin_packed_struct struct bt_att_signed_write_cmd_s +{ + uint16_t handle; + uint8_t value[0]; +} end_packed_struct; + +typedef void (*bt_att_func_t)(FAR struct bt_conn_s *conn, uint8_t err, + FAR const void *pdu, uint16_t length, + FAR void *user_data); +typedef void (*bt_att_destroy_t)(FAR void *user_data); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void bt_att_init(void); +struct bt_buf_s *bt_att_create_pdu(FAR struct bt_conn_s *conn, uint8_t op, + size_t len); + +/* Send ATT PDU over a connection */ + +int bt_att_send(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf, + bt_att_func_t func, FAR void *user_data, + bt_att_destroy_t destroy); + +/* Cancel ATT request */ + +void bt_att_cancel(FAR struct bt_conn_s *conn); + +#endif /* __WIRELESS_BLUETOOTH_BT_ATTR_H */ diff --git a/wireless/bluetooth/bt_buf.c b/wireless/bluetooth/bt_buf.c new file mode 100644 index 00000000000..5611949574b --- /dev/null +++ b/wireless/bluetooth/bt_buf.c @@ -0,0 +1,221 @@ +/**************************************************************************** + * wireless/bluetooth/bt_buf_s.c + * Bluetooth buffer management + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bt_hcicore.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +FAR struct bt_buf_s *bt_buf_get(enum bt_buf_type_e type, size_t reserve_head) +{ + FAR struct bt_buf_s *buf; + FAR struct iob_s *iob; + + wlinfo("type %d reserve %u\n", type, reserve_head); + + iob = iob_alloc(false); + if (iob == NULL) + { + if (up_interrupt_context()) + { + wlerr("ERROR: Failed to get free buffer\n"); + return NULL; + } + + wlwarn("WARNING: Low on buffers. Waiting (type %d)\n", type); + iob = iob_alloc(false); + } + + iob->io_len = sizeof(struct bt_buf_s); + iob->io_offset = 0; + iob->io_pktlen = sizeof(struct bt_buf_s); + + buf = (FAR struct bt_buf_s *)iob->io_data; + memset(buf, 0, sizeof(struct bt_buf_s)); + + buf->iob = iob; + buf->ref = 1; + buf->type = type; + buf->data = buf->buf + reserve_head; + + wlinfo("buf %p type %d reserve %u\n", buf, buf->type, reserve_head); + return buf; +} + +void bt_buf_put(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_cp_host_num_completed_packets_s *cp; + FAR struct bt_hci_handle_count_s *hc; + enum bt_buf_type_e type; + uint16_t handle; + + wlinfo("buf %p ref %u type %d\n", buf, buf->ref, buf->type); + + if (--buf->ref > 0) + { + return; + } + + handle = buf->u.acl.handle; + type = buf->type; + + DEBUGASSERT(buf->iob != NULL); + iob_free(buf->iob); + + if (type != BT_ACL_IN) + { + return; + } + + wlinfo("Reporting completed packet for handle %u\n", handle); + + buf = bt_hci_cmd_create(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, + sizeof(*cp) + sizeof(*hc)); + if (!buf) + { + wlerr("ERROR: Unable to allocate new HCI command\n"); + return; + } + + cp = bt_buf_add(buf, sizeof(*cp)); + cp->num_handles = BT_HOST2LE16(1); + + hc = bt_buf_add(buf, sizeof(*hc)); + hc->handle = BT_HOST2LE16(handle); + hc->count = BT_HOST2LE16(1); + + bt_hci_cmd_send(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, buf); +} + +FAR struct bt_buf_s *bt_buf_hold(FAR struct bt_buf_s *buf) +{ + wlinfo("buf %p (old) ref %u type %d\n", buf, buf->ref, buf->type); + buf->ref++; + return buf; +} + +FAR void *bt_buf_add(FAR struct bt_buf_s *buf, size_t len) +{ + FAR uint8_t *tail = bt_buf_tail(buf); + + wlinfo("buf %p len %u\n", buf, len); + + DEBUGASSERT(bt_buf_tailroom(buf) >= len); + + buf->len += len; + return tail; +} + +void bt_buf_add_le16(FAR struct bt_buf_s *buf, uint16_t value) +{ + wlinfo("buf %p value %u\n", buf, value); + + value = BT_HOST2LE16(value); + memcpy(bt_buf_add(buf, sizeof(value)), &value, sizeof(value)); +} + +FAR void *bt_buf_push(FAR struct bt_buf_s *buf, size_t len) +{ + wlinfo("buf %p len %u\n", buf, len); + + DEBUGASSERT(bt_buf_headroom(buf) >= len); + + buf->data -= len; + buf->len += len; + return buf->data; +} + +FAR void *bt_buf_pull(FAR struct bt_buf_s *buf, size_t len) +{ + wlinfo("buf %p len %u\n", buf, len); + + DEBUGASSERT(buf->len >= len); + + buf->len -= len; + return buf->data += len; +} + +uint16_t bt_buf_pull_le16(FAR struct bt_buf_s * buf) +{ + uint16_t value; + + value = BT_GETUINT16((FAR uint8_t *)buf->data); + bt_buf_pull(buf, sizeof(value)); + + return BT_LE162HOST(value); +} + +size_t bt_buf_headroom(FAR struct bt_buf_s * buf) +{ + return buf->data - buf->buf; +} + +size_t bt_buf_tailroom(FAR struct bt_buf_s * buf) +{ + return BT_BUF_MAX_DATA - bt_buf_headroom(buf) - buf->len; +} + +int bt_buf_init(void) +{ + wlinfo("Configured IOBs: IOBs: %u size: %u (%u)\n", + CONFIG_IOB_NBUFFERS, CONFIG_IOB_BUFSIZE, sizeof(struct bt_buf_s)); + + DEBUGASSERT(sizeof(struct bt_buf_s) <= CONFIG_IOB_BUFSIZE); + return 0; +} diff --git a/wireless/bluetooth/bt_conn.c b/wireless/bluetooth/bt_conn.c new file mode 100644 index 00000000000..1d01159fa6f --- /dev/null +++ b/wireless/bluetooth/bt_conn.c @@ -0,0 +1,808 @@ +/**************************************************************************** + * wireless/bluetooth/bt_conn.c + * Bluetooth connection handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "bt_atomic.h" +#include "bt_queue.h" +#include "bt_hcicore.h" +#include "bt_conn.h" +#include "bt_l2cap.h" +#include "bt_keys.h" +#include "bt_smp.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct bt_conn_handoff_s +{ + sem_t sync_sem; + FAR struct bt_conn_s *conn; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct bt_conn_s g_conns[CONFIG_BLUETOOTH_MAX_CONN]; +static struct bt_conn_handoff_s g_conn_handoff = +{ + SEM_INITIALIZER(1), + NULL +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_WIRELESS_INFO +static const char *state2str(enum bt_conn_state_e state) +{ + switch (state) + { + case BT_CONN_DISCONNECTED: + return "disconnected"; + + case BT_CONN_CONNECT_SCAN: + return "connect-scan"; + + case BT_CONN_CONNECT: + return "connect"; + + case BT_CONN_CONNECTED: + return "connected"; + + case BT_CONN_DISCONNECT: + return "disconnect"; + + default: + return "(unknown)"; + } +} +#endif + +static void bt_conn_reset_rx_state(FAR struct bt_conn_s *conn) +{ + if (!conn->rx_len) + { + return; + } + + bt_buf_put(conn->rx); + conn->rx = NULL; + conn->rx_len = 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void bt_conn_recv(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf, uint8_t flags) +{ + FAR struct bt_l2cap_hdr_s *hdr; + uint16_t len; + + wlinfo("handle %u len %u flags %02x\n", conn->handle, buf->len, flags); + + /* Check packet boundary flags */ + switch (flags) + { + case 0x02: + /* First packet */ + + hdr = (void *)buf->data; + len = BT_LE162HOST(hdr->len); + + wlinfo("First, len %u final %u\n", buf->len, len); + + if (conn->rx_len) + { + wlerr("ERROR: Unexpected first L2CAP frame\n"); + bt_conn_reset_rx_state(conn); + } + + conn->rx_len = (sizeof(*hdr) + len) - buf->len; + wlinfo("rx_len %u\n", conn->rx_len); + if (conn->rx_len) + { + conn->rx = buf; + return; + } + + break; + + case 0x01: + /* Continuation */ + + if (!conn->rx_len) + { + wlerr("ERROR: Unexpected L2CAP continuation\n"); + bt_conn_reset_rx_state(conn); + bt_buf_put(buf); + return; + } + + if (buf->len > conn->rx_len) + { + wlerr("ERROR: L2CAP data overflow\n"); + bt_conn_reset_rx_state(conn); + bt_buf_put(buf); + return; + } + + wlinfo("Cont, len %u rx_len %u\n", buf->len, conn->rx_len); + + if (buf->len > bt_buf_tailroom(conn->rx)) + { + wlerr("ERROR: Not enough buffer space for L2CAP data\n"); + bt_conn_reset_rx_state(conn); + bt_buf_put(buf); + return; + } + + memcpy(bt_buf_add(conn->rx, buf->len), buf->data, buf->len); + conn->rx_len -= buf->len; + bt_buf_put(buf); + + if (conn->rx_len) + { + return; + } + + buf = conn->rx; + conn->rx = NULL; + conn->rx_len = 0; + + break; + + default: + wlerr("ERROR: Unexpected ACL flags (0x%02x)\n", flags); + bt_conn_reset_rx_state(conn); + bt_buf_put(buf); + return; + } + + hdr = (void *)buf->data; + len = BT_LE162HOST(hdr->len); + + if (sizeof(*hdr) + len != buf->len) + { + wlerr("ERROR: ACL len mismatch (%u != %u)\n", len, buf->len); + bt_buf_put(buf); + return; + } + + wlinfo("Successfully parsed %u byte L2CAP packet\n", buf->len); + + bt_l2cap_recv(conn, buf); +} + +void bt_conn_send(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_acl_hdr_s *hdr; + sq_queue_t fraglist; + sq_entry_t *fragment; + uint16_t len; + uint16_t remaining = buf->len; + FAR uint8_t *ptr; + + DEBUGASSERT(conn != NULL && buf != NULL); + wlinfo("conn handle %u buf len %u\n", conn->handle, buf->len); + + if (conn->state != BT_CONN_CONNECTED) + { + wlerr("ERROR: not connected!\n"); + return; + } + + len = remaining; + if (len > g_btdev.le_mtu) + { + len = g_btdev.le_mtu; + } + + hdr = bt_buf_push(buf, sizeof(*hdr)); + hdr->handle = BT_HOST2LE16(conn->handle); + hdr->len = BT_HOST2LE16(len); + + buf->len -= remaining - len; + ptr = bt_buf_tail(buf); + + /* Add the fragment to the end of the list */ + + fragment = (FAR sq_entry_t *)buf->iob; + DEBUGASSERT(fragment != NULL); + sq_addlast(fragment, &fraglist); + + remaining -= len; + + while (remaining) + { + buf = bt_l2cap_create_pdu(conn); + + len = remaining; + if (len < g_btdev.le_mtu) + { + len = g_btdev.le_mtu; + } + + /* Copy from original buffer */ + + memcpy(bt_buf_add(buf, len), ptr, len); + ptr += len; + + hdr = bt_buf_push(buf, sizeof(*hdr)); + hdr->handle = BT_HOST2LE16(conn->handle | (1 << 12)); + hdr->len = BT_HOST2LE16(len); + + /* Add the fragment to the end of the list */ + + fragment = (FAR sq_entry_t *)buf->iob; + DEBUGASSERT(fragment != NULL); + sq_addlast(fragment, &fraglist); + + remaining -= len; + } + + /* Then send each fragment in the correct order */ + + while ((fragment = sq_remfirst(&fraglist)) != NULL) + { + buf = (FAR struct bt_buf_s *)(((FAR struct iob_s *)fragment)->io_data); + bt_queue_send(conn->tx_queue, buf, BT_NORMAL_PRIO); + } +} + +static int conn_tx_kthread(int argc, FAR char *argv[]) +{ + FAR struct bt_conn_s *conn; + FAR struct bt_buf_s *buf; + int ret; + + /* Get the connection instance */ + + conn = g_conn_handoff.conn; + DEBUGASSERT(conn != NULL); + nxsem_post(&g_conn_handoff.sync_sem); + + wlinfo("Started for handle %u\n", conn->handle); + + while (conn->state == BT_CONN_CONNECTED) + { + /* Wait until the controller can accept ACL packets */ + + wlinfo("calling nxsem_wait\n"); + + do + { + ret = nxsem_wait(&g_btdev.le_pkts_sem); + } + while (ret == -EINTR); + + DEBUGASSERT(ret == OK); + + /* Check for disconnection */ + + if (conn->state != BT_CONN_CONNECTED) + { + nxsem_post(&g_btdev.le_pkts_sem); + break; + } + + /* Get next ACL packet for connection */ + + ret = bt_queue_recv(conn->tx_queue, &buf); + DEBUGASSERT(ret >= 0 && buf != NULL); + UNUSED(ret); + + if (conn->state != BT_CONN_CONNECTED) + { + nxsem_post(&g_btdev.le_pkts_sem); + bt_buf_put(buf); + break; + } + + wlinfo("passing buf %p len %u to driver\n", buf, buf->len); + g_btdev.dev->send(buf); + bt_buf_put(buf); + } + + wlinfo("handle %u disconnected - cleaning up\n", conn->handle); + + /* Give back any allocated buffers */ + + do + { + buf = NULL; + ret = bt_queue_recv(conn->tx_queue, &buf); + if (ret >= 0) + { + DEBUGASSERT(buf != NULL); + bt_buf_put(buf); + } + } + while (ret >= OK); + + bt_conn_reset_rx_state(conn); + + wlinfo("handle %u exiting\n", conn->handle); + bt_conn_put(conn); + return EXIT_SUCCESS; +} + +FAR struct bt_conn_s *bt_conn_add(FAR const bt_addr_le_t *peer, + uint8_t role) +{ + FAR struct bt_conn_s *conn = NULL; + int i; + + for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++) + { + if (!bt_addr_le_cmp(&g_conns[i].dst, BT_ADDR_LE_ANY)) + { + conn = &g_conns[i]; + break; + } + } + + if (!conn) + { + return NULL; + } + + memset(conn, 0, sizeof(*conn)); + + bt_atomic_set(&conn->ref, 1); + conn->role = role; + bt_addr_le_copy(&conn->dst, peer); + + return conn; +} + +void bt_conn_set_state(FAR struct bt_conn_s *conn, + enum bt_conn_state_e state) +{ + enum bt_conn_state_e old_state; + + wlinfo("%s -> %s\n", state2str(conn->state), state2str(state)); + + if (conn->state == state) + { + wlwarn("no transition\n"); + return; + } + + old_state = conn->state; + conn->state = state; + + /* Take a reference for the first state transition after bt_conn_add() and + * keep it until reaching DISCONNECTED again. + */ + + if (old_state == BT_CONN_DISCONNECTED) + { + bt_conn_get(conn); + } + + switch (conn->state) + { + case BT_CONN_CONNECTED: + { + pid_t pid; + int ret; + + ret = bt_queue_open(BT_CONN_TX, O_RDWR | O_CREAT, + CONFIG_BLUETOOTH_TXCONN_NMSGS, &conn->tx_queue); + DEBUGASSERT(ret >= 0 && g_btdev.tx_queue != 0); + UNUSED(ret); + + /* Get exclusive access to the handoff structure. The count will be + * zero when we complete this. + */ + + do + { + ret = nxsem_wait(&g_conn_handoff.sync_sem); + } + while (ret == -EINTR); + + DEBUGASSERT(ret == OK); + + /* Start the Tx connection kernel thread */ + + g_conn_handoff.conn = bt_conn_get(conn); + pid = kthread_create("BT Conn Tx", CONFIG_BLUETOOTH_TXCONN_PRIORITY, + CONFIG_BLUETOOTH_TXCONN_STACKSIZE, + conn_tx_kthread, NULL); + DEBUGASSERT(pid > 0); + + /* Take the semaphore again. This will force us to wait with the + * sem_count at -1. It will be zero again when we continue. + */ + + do + { + ret = nxsem_wait(&g_conn_handoff.sync_sem); + } + while (ret == -EINTR); + + DEBUGASSERT(ret == OK); + nxsem_post(&g_conn_handoff.sync_sem); + } + break; + + case BT_CONN_DISCONNECTED: + /* Send dummy buffer to wake up and stop the Tx thread for states where it + * was running. + */ + + if (old_state == BT_CONN_CONNECTED || old_state == BT_CONN_DISCONNECT) + { + bt_queue_send(conn->tx_queue, bt_buf_get(BT_DUMMY, 0), BT_NORMAL_PRIO); + } + + /* Release the reference we took for the very first state transition. */ + + bt_conn_put(conn); + break; + + case BT_CONN_CONNECT_SCAN: + case BT_CONN_CONNECT: + case BT_CONN_DISCONNECT: + break; + + default: + wlwarn("no valid (%u) state was set\n", state); + break; + } +} + +FAR struct bt_conn_s *bt_conn_lookup_handle(uint16_t handle) +{ + int i; + + for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++) + { + /* We only care about connections with a valid handle */ + + if (g_conns[i].state != BT_CONN_CONNECTED && + g_conns[i].state != BT_CONN_DISCONNECT) + { + continue; + } + + if (g_conns[i].handle == handle) + { + return bt_conn_get(&g_conns[i]); + } + } + + return NULL; +} + +FAR struct bt_conn_s *bt_conn_lookup_addr_le(FAR const bt_addr_le_t * peer) +{ + int i; + + for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++) + { + if (!bt_addr_le_cmp(peer, &g_conns[i].dst)) + { + return bt_conn_get(&g_conns[i]); + } + } + + return NULL; +} + +FAR struct bt_conn_s *bt_conn_lookup_state(FAR const bt_addr_le_t * peer, + enum bt_conn_state_e state) +{ + int i; + + for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++) + { + if (!bt_addr_le_cmp(&g_conns[i].dst, BT_ADDR_LE_ANY)) + { + continue; + } + + if (bt_addr_le_cmp(peer, BT_ADDR_LE_ANY) && + bt_addr_le_cmp(peer, &g_conns[i].dst)) + { + continue; + } + + if (g_conns[i].state == state) + { + return bt_conn_get(&g_conns[i]); + } + } + + return NULL; +} + +FAR struct bt_conn_s *bt_conn_get(FAR struct bt_conn_s *conn) +{ + bt_atomic_incr(&conn->ref); + + wlinfo("handle %u ref %u\n", conn->handle, bt_atomic_get(&conn->ref)); + + return conn; +} + +void bt_conn_put(FAR struct bt_conn_s *conn) +{ + bt_atomic_t old_ref; + + old_ref = bt_atomic_decr(&conn->ref); + + wlinfo("handle %u ref %u\n", conn->handle, bt_atomic_get(&conn->ref)); + + if (old_ref > 1) + { + return; + } + + bt_addr_le_copy(&conn->dst, BT_ADDR_LE_ANY); +} + +const bt_addr_le_t *bt_conn_get_dst(FAR const struct bt_conn_s *conn) +{ + return &conn->dst; +} + +int bt_conn_security(FAR struct bt_conn_s *conn, enum bt_security_e sec) +{ + FAR struct bt_keys_s *keys; + + if (conn->state != BT_CONN_CONNECTED) + { + return -ENOTCONN; + } + + /* Nothing to do */ + + if (sec == BT_SECURITY_LOW) + { + return 0; + } + + /* For now we only support JustWorks */ + + if (sec > BT_SECURITY_MEDIUM) + { + return -EINVAL; + } + + if (conn->encrypt) + { + return 0; + } + + if (conn->role == BT_HCI_ROLE_SLAVE) + { + return bt_smp_send_security_req(conn); + } + + keys = bt_keys_find(BT_KEYS_LTK, &conn->dst); + if (keys) + { + return bt_conn_le_start_encryption(conn, keys->ltk.rand, + keys->ltk.ediv, keys->ltk.val); + } + + return bt_smp_send_pairing_req(conn); +} + +void bt_conn_set_auto_conn(FAR struct bt_conn_s *conn, bool auto_conn) +{ + if (auto_conn) + { + bt_atomic_setbit(conn->flags, BT_CONN_AUTO_CONNECT); + } + else + { + bt_atomic_clrbit(conn->flags, BT_CONN_AUTO_CONNECT); + } +} + +static int bt_hci_disconnect(FAR struct bt_conn_s *conn, uint8_t reason) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_hci_cp_disconnect_s *disconn; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_DISCONNECT, sizeof(*disconn)); + if (!buf) + { + return -ENOBUFS; + } + + disconn = bt_buf_add(buf, sizeof(*disconn)); + disconn->handle = BT_HOST2LE16(conn->handle); + disconn->reason = reason; + + err = bt_hci_cmd_send(BT_HCI_OP_DISCONNECT, buf); + if (err) + { + return err; + } + + bt_conn_set_state(conn, BT_CONN_DISCONNECT); + return 0; +} + +static int bt_hci_connect_le_cancel(FAR struct bt_conn_s *conn) +{ + int err; + + err = bt_hci_cmd_send(BT_HCI_OP_LE_CREATE_CONN_CANCEL, NULL); + if (err) + { + return err; + } + + return 0; +} + +int bt_conn_disconnect(FAR struct bt_conn_s *conn, uint8_t reason) +{ + /* Disconnection is initiated by us, so auto connection shall be disabled. + * Otherwise the passive scan would be enabled and we could send LE Create + * Connection as soon as the remote starts advertising. + */ + + bt_conn_set_auto_conn(conn, false); + + switch (conn->state) + { + case BT_CONN_CONNECT_SCAN: + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + bt_le_scan_update(); + return 0; + + case BT_CONN_CONNECT: + return bt_hci_connect_le_cancel(conn); + + case BT_CONN_CONNECTED: + return bt_hci_disconnect(conn, reason); + + case BT_CONN_DISCONNECT: + return 0; + + case BT_CONN_DISCONNECTED: + default: + return -ENOTCONN; + } +} + +FAR struct bt_conn_s *bt_conn_create_le(FAR const bt_addr_le_t *peer) +{ + FAR struct bt_conn_s *conn; + + conn = bt_conn_lookup_addr_le(peer); + if (conn) + { + switch (conn->state) + { + case BT_CONN_CONNECT_SCAN: + case BT_CONN_CONNECT: + case BT_CONN_CONNECTED: + return conn; + + default: + bt_conn_put(conn); + return NULL; + } + } + + conn = bt_conn_add(peer, BT_HCI_ROLE_MASTER); + if (!conn) + { + return NULL; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); + bt_le_scan_update(); + return conn; +} + +int bt_conn_le_start_encryption(FAR struct bt_conn_s *conn, uint64_t rand, + uint16_t ediv, FAR const uint8_t *ltk) +{ + FAR struct bt_hci_cp_le_start_encryption_s *cp; + FAR struct bt_buf_s *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_START_ENCRYPTION, sizeof(*cp)); + if (!buf) + { + return -ENOBUFS; + } + + cp = bt_buf_add(buf, sizeof(*cp)); + cp->handle = BT_HOST2LE16(conn->handle); + cp->rand = rand; + cp->ediv = ediv; + memcpy(cp->ltk, ltk, sizeof(cp->ltk)); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_START_ENCRYPTION, buf, NULL); +} + +int bt_conn_le_conn_update(FAR struct bt_conn_s *conn, uint16_t min, + uint16_t max, uint16_t latency, uint16_t timeout) +{ + FAR struct hci_cp_le_conn_update_s *conn_update; + FAR struct bt_buf_s *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_UPDATE, sizeof(*conn_update)); + if (!buf) + { + return -ENOBUFS; + } + + conn_update = bt_buf_add(buf, sizeof(*conn_update)); + memset(conn_update, 0, sizeof(*conn_update)); + conn_update->handle = BT_HOST2LE16(conn->handle); + conn_update->conn_interval_min = BT_HOST2LE16(min); + conn_update->conn_interval_max = BT_HOST2LE16(max); + conn_update->conn_latency = BT_HOST2LE16(latency); + conn_update->supervision_timeout = BT_HOST2LE16(timeout); + + return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_UPDATE, buf); +} diff --git a/wireless/bluetooth/bt_conn.h b/wireless/bluetooth/bt_conn.h new file mode 100644 index 00000000000..822411557a6 --- /dev/null +++ b/wireless/bluetooth/bt_conn.h @@ -0,0 +1,156 @@ +/**************************************************************************** + * wireless/bluetooth/bt_conn.h + * Bluetooth connection handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __WIRELESS_BLUETOOTH_BT_CONN_H +#define __WIRELESS_BLUETOOTH_BT_CONN_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include "bt_atomic.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum bt_conn_state_e +{ + BT_CONN_DISCONNECTED, + BT_CONN_CONNECT_SCAN, + BT_CONN_CONNECT, + BT_CONN_CONNECTED, + BT_CONN_DISCONNECT, +}; + +/* L2CAP signaling channel specific context */ + +struct bt_conn_l2cap_s +{ + uint8_t ident; +}; + +/* bt_conn_s flags: the flags defined here represent connection parameters */ + +enum bt_conn_flags_e +{ + BT_CONN_AUTO_CONNECT, +}; + +struct bt_conn_s +{ + uint16_t handle; + uint8_t role; + bt_atomic_t flags[1]; + + bt_addr_le_t src; + bt_addr_le_t dst; + + uint8_t encrypt; + + uint16_t rx_len; + FAR struct bt_buf_s *rx; + + /* Queue for outgoing ACL data */ + + mqd_t tx_queue; + + FAR struct bt_keys_s *keys; + + /* Fixed channel contexts */ + + struct bt_conn_l2cap_s l2cap; + FAR void *att; + FAR void *smp; + + uint8_t le_conn_interval; + bt_atomic_t ref; + enum bt_conn_state_e state; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Process incoming data for a connection */ + +void bt_conn_recv(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf, + uint8_t flags); + +/* Send data over a connection */ + +void bt_conn_send(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf); + +/* Add a new connection */ + +FAR struct bt_conn_s *bt_conn_add(FAR const bt_addr_le_t *peer, uint8_t role); + +/* Look up an existing connection */ + +FAR struct bt_conn_s *bt_conn_lookup_handle(uint16_t handle); + +/* Look up a connection state. For BT_ADDR_LE_ANY, returns the first connection + * with the specific state + */ + +FAR struct bt_conn_s *bt_conn_lookup_state(FAR const bt_addr_le_t * peer, + enum bt_conn_state_e state); + +/* Set connection object in certain state and perform action related to state */ + +void bt_conn_set_state(FAR struct bt_conn_s *conn, enum bt_conn_state_e state); + +/* rand and ediv should be in BT order */ + +int bt_conn_le_start_encryption(FAR struct bt_conn_s *conn, uint64_t rand, + uint16_t ediv, FAR const uint8_t *ltk); + +int bt_conn_le_conn_update(FAR struct bt_conn_s *conn, uint16_t min, + uint16_t max, uint16_t latency, + uint16_t timeout); + +#endif /* __WIRELESS_BLUETOOTH_BT_CONN_H */ diff --git a/wireless/bluetooth/bt_gatt.c b/wireless/bluetooth/bt_gatt.c new file mode 100644 index 00000000000..cff95754a1f --- /dev/null +++ b/wireless/bluetooth/bt_gatt.c @@ -0,0 +1,1213 @@ +/**************************************************************************** + * wireless/bluetooth/bt_gatt.c + * Generic Attribute Profile handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bt_hcicore.h" +#include "bt_conn.h" +#include "bt_keys.h" +#include "bt_l2cap.h" +#include "bt_att.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +begin_packed_struct struct gatt_incl_s +{ + uint16_t start_handle; + uint16_t end_handle; + union + { + uint16_t uuid16; + uint8_t uuid[16]; + } u; +} end_packed_struct; + +begin_packed_struct struct gatt_chrc_s +{ + uint8_t properties; + uint16_t value_handle; + union + { + uint16_t uuid16; + uint8_t uuid[16]; + } u; +} end_packed_struct; + +struct notify_data_s +{ + FAR const void *data; + size_t len; + uint8_t handle; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR const struct bt_gatt_attr_s *g_db = NULL; +static size_t g_attr_count = 0; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void bt_gatt_register(FAR const struct bt_gatt_attr_s *attrs, size_t count) +{ + g_db = attrs; + g_attr_count = count; +} + +int bt_gatt_attr_read(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, FAR void *buf, + uint8_t buf_len, uint16_t offset, + FAR const void *value, uint8_t value_len) +{ + uint8_t maxlen; + uint8_t len; + + if (offset > value_len) + { + return -EINVAL; + } + + len = buf_len; + maxlen = value_len - offset; + if (len > maxlen) + { + len = maxlen; + } + + wlinfo("handle 0x%04x offset %u length %u\n", attr->handle, offset, len); + + memcpy(buf, value + offset, len); + return len; +} + +int bt_gatt_attr_read_service(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR void *buf, uint8_t len, uint16_t offset) +{ + FAR struct bt_uuid_s *uuid = attr->user_data; + + if (uuid->type == BT_UUID_16) + { + uint16_t uuid16 = BT_HOST2LE16(uuid->u.u16); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &uuid16, + sizeof(uuid16)); + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, uuid->u.u128, + sizeof(uuid->u.u128)); +} + +int bt_gatt_attr_read_include(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR void *buf, uint8_t len, uint16_t offset) +{ + FAR struct bt_gatt_include_s *incl = attr->user_data; + struct gatt_incl_s pdu; + uint8_t value_len; + + pdu.start_handle = BT_HOST2LE16(incl->start_handle); + pdu.end_handle = BT_HOST2LE16(incl->end_handle); + value_len = sizeof(pdu.start_handle) + sizeof(pdu.end_handle); + + if (incl->uuid->type == BT_UUID_16) + { + pdu.u.uuid16 = BT_HOST2LE16(incl->uuid->u.u16); + value_len += sizeof(pdu.u.uuid16); + } + else + { + memcpy(pdu.u.uuid, incl->uuid->u.u128, sizeof(incl->uuid->u.u128)); + value_len += sizeof(incl->uuid->u.u128); + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len); +} + +int bt_gatt_attr_read_chrc(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR void *buf, uint8_t len, uint16_t offset) +{ + FAR struct bt_gatt_chrc_s *chrc = attr->user_data; + struct gatt_chrc_s pdu; + uint8_t value_len; + + pdu.properties = chrc->properties; + pdu.value_handle = BT_HOST2LE16(chrc->value_handle); + value_len = sizeof(pdu.properties) + sizeof(pdu.value_handle); + + if (chrc->uuid->type == BT_UUID_16) + { + pdu.u.uuid16 = BT_HOST2LE16(chrc->uuid->u.u16); + value_len += sizeof(pdu.u.uuid16); + } + else + { + memcpy(pdu.u.uuid, chrc->uuid->u.u128, sizeof(chrc->uuid->u.u128)); + value_len += sizeof(chrc->uuid->u.u128); + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len); +} + +void bt_gatt_foreach_attr(uint16_t start_handle, uint16_t end_handle, + bt_gatt_attr_func_t func, FAR void *user_data) +{ + size_t i; + + for (i = 0; i < g_attr_count; i++) + { + FAR const struct bt_gatt_attr_s *attr = &g_db[i]; + + /* Check if attribute handle is within range */ + + if (attr->handle < start_handle || attr->handle > end_handle) + { + continue; + } + + if (func(attr, user_data) == BT_GATT_ITER_STOP) + { + break; + } + } +} + +int bt_gatt_attr_read_ccc(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR void *buf, uint8_t len, uint16_t offset) +{ + FAR struct _bt_gatt_ccc_s *ccc = attr->user_data; + uint16_t value; + size_t i; + + for (i = 0; i < ccc->cfg_len; i++) + { + if (bt_addr_le_cmp(&ccc->cfg[i].peer, &conn->dst)) + { + continue; + } + + value = BT_HOST2LE16(ccc->cfg[i].value); + break; + } + + /* Default to disable if there is no cfg for the peer */ + + if (i == ccc->cfg_len) + { + value = 0x0000; + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &value, + sizeof(value)); +} + +static void gatt_ccc_changed(FAR struct _bt_gatt_ccc_s *ccc) +{ + uint16_t value = 0x0000; + int i; + + for (i = 0; i < ccc->cfg_len; i++) + { + if (ccc->cfg[i].value > value) + { + value = ccc->cfg[i].value; + } + } + + wlinfo("ccc %p value 0x%04x\n", ccc, value); + + if (value != ccc->value) + { + ccc->value = value; + ccc->cfg_changed(value); + } +} + +int bt_gatt_attr_write_ccc(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR const void *buf, uint8_t len, + uint16_t offset) +{ + FAR struct _bt_gatt_ccc_s *ccc = attr->user_data; + FAR const uint16_t *data = buf; + bool bonded; + size_t i; + + if (len != sizeof(*data) || offset) + { + return -EINVAL; + } + + if (bt_keys_get_addr(&conn->dst)) + { + bonded = true; + } + else + { + bonded = false; + } + + for (i = 0; i < ccc->cfg_len; i++) + { + /* Check for existing configuration */ + + if (!bt_addr_le_cmp(&ccc->cfg[i].peer, &conn->dst)) + { + break; + } + } + + if (i == ccc->cfg_len) + { + for (i = 0; i < ccc->cfg_len; i++) + { + /* Check for unused configuration */ + + if (!ccc->cfg[i].valid) + { + bt_addr_le_copy(&ccc->cfg[i].peer, &conn->dst); + + /* Only set valid if bonded */ + + ccc->cfg[i].valid = bonded; + break; + } + } + + if (i == ccc->cfg_len) + { + wlwarn("No space to store CCC cfg\n"); + return -ENOMEM; + } + } + + ccc->cfg[i].value = BT_LE162HOST(*data); + + wlinfo("handle 0x%04x value %u\n", attr->handle, ccc->cfg[i].value); + + /* Update cfg if don't match */ + + if (ccc->cfg[i].value != ccc->value) + { + gatt_ccc_changed(ccc); + } + + return len; +} + +int bt_gatt_attr_read_cep(FAR struct bt_conn_s *conn, + FAR const struct bt_gatt_attr_s *attr, + FAR void *buf, uint8_t len, uint16_t offset) +{ + FAR struct bt_gatt_cep_s *value = attr->user_data; + uint16_t props = BT_HOST2LE16(value->properties); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &props, + sizeof(props)); +} + +static uint8_t notify_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct notify_data_s *data = user_data; + const struct bt_uuid_s uuid = + { + BT_UUID_16, + { + BT_UUID_GATT_CCC + } + }; + + const struct bt_uuid_s chrc = + { + BT_UUID_16, + { + BT_UUID_GATT_CHRC + } + }; + + FAR struct _bt_gatt_ccc_s *ccc; + size_t i; + + if (bt_uuid_cmp(attr->uuid, &uuid)) + { + /* Stop if we reach the next characteristic */ + + if (!bt_uuid_cmp(attr->uuid, &chrc)) + { + return BT_GATT_ITER_STOP; + } + + return BT_GATT_ITER_CONTINUE; + } + + /* Check attribute user_data must be of type struct _bt_gatt_ccc_s */ + + if (attr->write != bt_gatt_attr_write_ccc) + { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* Notify all peers configured */ + + for (i = 0; i < ccc->cfg_len; i++) + { + FAR struct bt_conn_s *conn; + FAR struct bt_buf_s *buf; + FAR struct bt_att_notify_s *nfy; + + /* TODO: Handle indications */ + + if (ccc->value != BT_GATT_CCC_NOTIFY) + { + continue; + } + + conn = bt_conn_lookup_addr_le(&ccc->cfg[i].peer); + if (!conn || conn->state != BT_CONN_CONNECTED) + { + continue; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_NOTIFY, + sizeof(*nfy) + data->len); + if (!buf) + { + wlwarn("No buffer available to send notification"); + bt_conn_put(conn); + return BT_GATT_ITER_STOP; + } + + wlinfo("conn %p handle 0x%04x\n", conn, data->handle); + + nfy = bt_buf_add(buf, sizeof(*nfy)); + nfy->handle = BT_HOST2LE16(data->handle); + + bt_buf_add(buf, data->len); + memcpy(nfy->value, data->data, data->len); + + bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf); + bt_conn_put(conn); + } + + return BT_GATT_ITER_CONTINUE; +} + +void bt_gatt_notify(uint16_t handle, FAR const void *data, size_t len) +{ + struct notify_data_s nfy; + + nfy.handle = handle; + nfy.data = data; + nfy.len = len; + + bt_gatt_foreach_attr(handle, 0xffff, notify_cb, &nfy); +} + +static uint8_t connected_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct bt_conn_s *conn = user_data; + FAR struct _bt_gatt_ccc_s *ccc; + size_t i; + + /* Check attribute user_data must be of type struct _bt_gatt_ccc_s */ + + if (attr->write != bt_gatt_attr_write_ccc) + { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* If already enabled skip */ + + if (ccc->value) + { + return BT_GATT_ITER_CONTINUE; + } + + for (i = 0; i < ccc->cfg_len; i++) + { + /* Ignore configuration for different peer */ + + if (bt_addr_le_cmp(&conn->dst, &ccc->cfg[i].peer)) + { + continue; + } + + if (ccc->cfg[i].value) + { + gatt_ccc_changed(ccc); + return BT_GATT_ITER_CONTINUE; + } + } + + return BT_GATT_ITER_CONTINUE; +} + +void bt_gatt_connected(FAR struct bt_conn_s *conn) +{ + wlinfo("conn %p\n", conn); + bt_gatt_foreach_attr(0x0001, 0xffff, connected_cb, conn); +} + +static uint8_t disconnected_cb(FAR const struct bt_gatt_attr_s *attr, + FAR void *user_data) +{ + FAR struct bt_conn_s *conn = user_data; + FAR struct _bt_gatt_ccc_s *ccc; + size_t i; + + /* Check attribute user_data must be of type struct _bt_gatt_ccc_s */ + + if (attr->write != bt_gatt_attr_write_ccc) + { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* If already disabled skip */ + + if (!ccc->value) + { + return BT_GATT_ITER_CONTINUE; + } + + for (i = 0; i < ccc->cfg_len; i++) + { + /* Ignore configurations with disabled value */ + + if (!ccc->cfg[i].value) + { + continue; + } + + if (bt_addr_le_cmp(&conn->dst, &ccc->cfg[i].peer)) + { + FAR struct bt_conn_s *tmp; + + /* Skip if there is another peer connected */ + + tmp = bt_conn_lookup_addr_le(&ccc->cfg[i].peer); + if (tmp && tmp->state == BT_CONN_CONNECTED) + { + bt_conn_put(tmp); + return BT_GATT_ITER_CONTINUE; + } + } + } + + /* Reset value while disconnected */ + + memset(&ccc->value, 0, sizeof(ccc->value)); + ccc->cfg_changed(ccc->value); + + wlinfo("ccc %p reseted\n", ccc); + return BT_GATT_ITER_CONTINUE; +} + +void bt_gatt_disconnected(FAR struct bt_conn_s *conn) +{ + wlinfo("conn %p\n", conn); + bt_gatt_foreach_attr(0x0001, 0xffff, disconnected_cb, conn); +} + +static void gatt_mtu_rsp(FAR struct bt_conn_s *conn, uint8_t err, + FAR const void *pdu, uint16_t length, + FAR void *user_data) +{ + bt_gatt_rsp_func_t func = user_data; + + func(conn, err); +} + +static int gatt_send(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf, + bt_att_func_t func, FAR void *user_data, + bt_att_destroy_t destroy) +{ + int err; + + err = bt_att_send(conn, buf, func, user_data, destroy); + if (err) + { + wlerr("ERROR: Error sending ATT PDU: %d\n", err); + bt_buf_put(buf); + } + + return err; +} + +int bt_gatt_exchange_mtu(FAR struct bt_conn_s *conn, + bt_gatt_rsp_func_t func) +{ + FAR struct bt_att_exchange_mtu_req_s *req; + FAR struct bt_buf_s *buf; + uint16_t mtu; + + if (!conn || !func) + { + return -EINVAL; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_MTU_REQ, sizeof(*req)); + if (!buf) + { + return -ENOMEM; + } + + /* Select MTU based on the amount of room we have in bt_buf_s including one + * extra byte for ATT header. + */ + + mtu = bt_buf_tailroom(buf) + 1; + + wlinfo("Client MTU %u\n", mtu); + + req = bt_buf_add(buf, sizeof(*req)); + req->mtu = BT_HOST2LE16(mtu); + + return gatt_send(conn, buf, gatt_mtu_rsp, func, NULL); +} + +static void att_find_type_rsp(FAR struct bt_conn_s *conn, uint8_t err, + FAR const void *pdu, uint16_t length, + FAR void *user_data) +{ + FAR const struct bt_att_find_type_rsp_s *rsp = pdu; + FAR struct bt_gatt_discover_params_s *params = user_data; + uint16_t end_handle = 0; + uint16_t start_handle; + uint8_t i; + + wlinfo("err 0x%02x\n", err); + + if (err) + { + goto done; + } + + /* Parse attributes found */ + + for (i = 0; length >= sizeof(rsp->list[i]); + i++, length -= sizeof(rsp->list[i])) + { + FAR const struct bt_gatt_attr_s *attr; + + start_handle = BT_LE162HOST(rsp->list[i].start_handle); + end_handle = BT_LE162HOST(rsp->list[i].end_handle); + + wlinfo("start_handle 0x%04x end_handle 0x%04x\n", start_handle, + end_handle); + + attr = + (&(struct bt_gatt_attr_s)BT_GATT_PRIMARY_SERVICE + (start_handle, params->uuid)); + + if (params->func(attr, params) == BT_GATT_ITER_STOP) + { + goto done; + } + } + + /* Stop if could not parse the whole PDU */ + + if (length > 0) + { + goto done; + } + + /* Stop if over the range or the requests */ + + if (end_handle >= params->end_handle) + { + goto done; + } + + /* Continue for the last found handle */ + + params->start_handle = end_handle; + if (!bt_gatt_discover(conn, params)) + { + return; + } + +done: + if (params->destroy) + { + params->destroy(params); + } +} + +int bt_gatt_discover(FAR struct bt_conn_s *conn, + FAR struct bt_gatt_discover_params_s *params) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_att_find_type_req_s *req; + FAR uint16_t *value; + + if (!conn || !params->uuid || !params->func || !params->start_handle || + !params->end_handle) + { + return -EINVAL; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_TYPE_REQ, sizeof(*req)); + if (!buf) + { + return -ENOMEM; + } + + req = bt_buf_add(buf, sizeof(*req)); + req->start_handle = BT_HOST2LE16(params->start_handle); + req->end_handle = BT_HOST2LE16(params->end_handle); + req->type = BT_HOST2LE16(BT_UUID_GATT_PRIMARY); + + wlinfo("uuid 0x%04x start_handle 0x%04x end_handle 0x%04x\n", + params->uuid->u.u16, params->start_handle, params->end_handle); + + switch (params->uuid->type) + { + case BT_UUID_16: + value = bt_buf_add(buf, sizeof(*value)); + *value = BT_HOST2LE16(params->uuid->u.u16); + break; + + case BT_UUID_128: + bt_buf_add(buf, sizeof(params->uuid->u.u128)); + memcpy(req->value, params->uuid->u.u128, sizeof(params->uuid->u.u128)); + break; + + default: + wlerr("ERROR: Unkown UUID type %u\n", params->uuid->type); + bt_buf_put(buf); + return -EINVAL; + } + + return gatt_send(conn, buf, att_find_type_rsp, params, NULL); +} + +static void att_read_type_rsp(FAR struct bt_conn_s *conn, uint8_t err, + FAR const void *pdu, uint16_t length, + FAR void *user_data) +{ + FAR const struct bt_att_read_type_rsp_s *rsp = pdu; + FAR struct bt_gatt_discover_params_s *params = user_data; + struct bt_uuid_s uuid; + struct bt_gatt_chrc_s value; + uint16_t handle = 0; + + wlinfo("err 0x%02x\n", err); + + if (err) + { + goto done; + } + + /* Data can be either in UUID16 or UUID128 */ + + switch (rsp->len) + { + case 7: /* UUID16 */ + uuid.type = BT_UUID_16; + break; + + case 21: /* UUID128 */ + uuid.type = BT_UUID_128; + break; + + default: + wlerr("ERROR: Invalid data len %u\n", rsp->len); + goto done; + } + + /* Parse characteristics found */ + + for (length--, pdu = rsp->data; length >= rsp->len; + length -= rsp->len, pdu += rsp->len) + { + FAR const struct bt_gatt_attr_s *attr; + FAR const struct bt_att_data_s *data = pdu; + FAR struct gatt_chrc_s *chrc = (FAR void *)data->value; + + handle = BT_LE162HOST(data->handle); + + /* Handle 0 is invalid */ + + if (!handle) + { + goto done; + } + + /* Convert characteristic data, bt_gatt_chrc and gatt_chrc_s have different + * formats so the conversion have to be done field by field. + */ + + value.properties = chrc->properties; + value.value_handle = BT_LE162HOST(chrc->value_handle); + value.uuid = &uuid; + + switch (uuid.type) + { + case BT_UUID_16: + uuid.u.u16 = BT_LE162HOST(chrc->u.uuid16); + break; + + case BT_UUID_128: + memcpy(uuid.u.u128, chrc->u.uuid, sizeof(chrc->u.uuid)); + break; + } + + wlinfo("handle 0x%04x properties 0x%02x value_handle 0x%04x\n", + handle, value.properties, value.value_handle); + + /* Skip if UUID is set but doesn't match */ + + if (params->uuid && bt_uuid_cmp(&uuid, params->uuid)) + { + continue; + } + + attr = (&(struct bt_gatt_attr_s)BT_GATT_CHARACTERISTIC(handle, &value)); + + if (params->func(attr, params) == BT_GATT_ITER_STOP) + { + goto done; + } + } + + /* Stop if could not parse the whole PDU */ + + if (length > 0) + { + goto done; + } + + /* Stop if over the requested range */ + + if (params->start_handle >= params->end_handle) + { + goto done; + } + + /* Continue to the next range */ + + if (!bt_gatt_discover_characteristic(conn, params)) + { + return; + } + +done: + if (params->destroy) + { + params->destroy(params); + } +} + +int bt_gatt_discover_characteristic(FAR struct bt_conn_s *conn, + FAR struct bt_gatt_discover_params_s *params) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_att_read_type_req_s *req; + FAR uint16_t *value; + + if (!conn || !params->func || !params->start_handle || !params->end_handle) + { + return -EINVAL; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_REQ, sizeof(*req)); + if (!buf) + { + return -ENOMEM; + } + + req = bt_buf_add(buf, sizeof(*req)); + req->start_handle = BT_HOST2LE16(params->start_handle); + req->end_handle = BT_HOST2LE16(params->end_handle); + + value = bt_buf_add(buf, sizeof(*value)); + *value = BT_HOST2LE16(BT_UUID_GATT_CHRC); + + wlinfo("start_handle 0x%04x end_handle 0x%04x\n", params->start_handle, + params->end_handle); + + return gatt_send(conn, buf, att_read_type_rsp, params, NULL); +} + +static void att_find_info_rsp(FAR struct bt_conn_s *conn, uint8_t err, + FAR const void *pdu, uint16_t length, + FAR void *user_data) +{ + FAR const struct bt_att_find_info_rsp_s *rsp = pdu; + FAR struct bt_gatt_discover_params_s *params = user_data; + struct bt_uuid_s uuid; + uint16_t handle = 0; + uint8_t len; + union + { + FAR const struct bt_att_info_16_s *i16; + FAR const struct bt_att_info_128_s *i128; + } info; + + wlinfo("err 0x%02x\n", err); + + if (err) + { + goto done; + } + + /* Data can be either in UUID16 or UUID128 */ + + switch (rsp->format) + { + case BT_ATT_INFO_16: + uuid.type = BT_UUID_16; + len = sizeof(info.i16); + break; + + case BT_ATT_INFO_128: + uuid.type = BT_UUID_128; + len = sizeof(info.i128); + break; + + default: + wlerr("ERROR: Invalid format %u\n", rsp->format); + goto done; + } + + /* Parse descriptors found */ + + for (length--, pdu = rsp->info; length >= len; length -= len, pdu += len) + { + FAR const struct bt_gatt_attr_s *attr; + + info.i16 = pdu; + handle = BT_LE162HOST(info.i16->handle); + + switch (uuid.type) + { + case BT_UUID_16: + uuid.u.u16 = BT_LE162HOST(info.i16->uuid); + break; + + case BT_UUID_128: + memcpy(uuid.u.u128, info.i128->uuid, sizeof(uuid.u.u128)); + break; + } + + wlinfo("handle 0x%04x\n", handle); + + /* Skip if UUID is set but doesn't match */ + + if (params->uuid && bt_uuid_cmp(&uuid, params->uuid)) + { + continue; + } + + attr = + (&(struct bt_gatt_attr_s)BT_GATT_DESCRIPTOR + (handle, &uuid, 0, NULL, NULL, NULL)); + + if (params->func(attr, params) == BT_GATT_ITER_STOP) + { + goto done; + } + } + + /* Stop if could not parse the whole PDU */ + + if (length > 0) + { + goto done; + } + + /* Next characteristic shall be after current value handle */ + + params->start_handle = handle; + if (params->start_handle < UINT16_MAX) + { + params->start_handle++; + } + + /* Stop if over the requested range */ + + if (params->start_handle >= params->end_handle) + { + goto done; + } + + /* Continue to the next range */ + + if (!bt_gatt_discover_descriptor(conn, params)) + { + return; + } + +done: + if (params->destroy) + { + params->destroy(params); + } +} + +int bt_gatt_discover_descriptor(FAR struct bt_conn_s *conn, + FAR struct bt_gatt_discover_params_s *params) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_att_find_info_req_s *req; + + if (!conn || !params->func || !params->start_handle || !params->end_handle) + { + return -EINVAL; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_INFO_REQ, sizeof(*req)); + if (!buf) + { + return -ENOMEM; + } + + req = bt_buf_add(buf, sizeof(*req)); + req->start_handle = BT_HOST2LE16(params->start_handle); + req->end_handle = BT_HOST2LE16(params->end_handle); + + wlinfo("start_handle 0x%04x end_handle 0x%04x\n", params->start_handle, + params->end_handle); + + return gatt_send(conn, buf, att_find_info_rsp, params, NULL); +} + +static void att_read_rsp(FAR struct bt_conn_s *conn, uint8_t err, + FAR const void *pdu, uint16_t length, + FAR void *user_data) +{ + bt_gatt_read_func_t func = user_data; + + wlinfo("err 0x%02x\n", err); + + if (err) + { + func(conn, err, NULL, 0); + return; + } + + func(conn, 0, pdu, length); +} + +static int gatt_read_blob(FAR struct bt_conn_s *conn, uint16_t handle, + uint16_t offset, bt_gatt_read_func_t func) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_att_read_blob_req_s *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_BLOB_REQ, sizeof(*req)); + if (!buf) + { + return -ENOMEM; + } + + req = bt_buf_add(buf, sizeof(*req)); + req->handle = BT_HOST2LE16(handle); + req->offset = BT_HOST2LE16(offset); + + wlinfo("handle 0x%04x offset 0x%04x\n", handle, offset); + + return gatt_send(conn, buf, att_read_rsp, func, NULL); +} + +int bt_gatt_read(FAR struct bt_conn_s *conn, uint16_t handle, + uint16_t offset, bt_gatt_read_func_t func) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_att_read_req_s *req; + + if (!conn || !handle || !func) + { + return -EINVAL; + } + + if (offset) + { + return gatt_read_blob(conn, handle, offset, func); + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_REQ, sizeof(*req)); + if (!buf) + { + return -ENOMEM; + } + + req = bt_buf_add(buf, sizeof(*req)); + req->handle = BT_HOST2LE16(handle); + + wlinfo("handle 0x%04x\n", handle); + + return gatt_send(conn, buf, att_read_rsp, func, NULL); +} + +static void att_write_rsp(FAR struct bt_conn_s *conn, uint8_t err, + FAR const void *pdu, uint16_t length, + FAR void *user_data) +{ + bt_gatt_rsp_func_t func = user_data; + + wlinfo("err 0x%02x\n", err); + + func(conn, err); +} + +static int gatt_write_cmd(FAR struct bt_conn_s *conn, uint16_t handle, + FAR const void *data, uint16_t length) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_att_write_cmd_s *cmd; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_CMD, sizeof(*cmd) + length); + if (!buf) + { + return -ENOMEM; + } + + cmd = bt_buf_add(buf, sizeof(*cmd)); + cmd->handle = BT_HOST2LE16(handle); + memcpy(cmd->value, data, length); + bt_buf_add(buf, length); + + wlinfo("handle 0x%04x length %u\n", handle, length); + + return gatt_send(conn, buf, NULL, NULL, NULL); +} + +int bt_gatt_write(FAR struct bt_conn_s *conn, uint16_t handle, + FAR const void *data, uint16_t length, + bt_gatt_rsp_func_t func) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_att_write_req_s *req; + + if (!conn || !handle) + { + return -EINVAL; + } + + if (!func) + { + return gatt_write_cmd(conn, handle, data, length); + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_REQ, sizeof(*req) + length); + if (!buf) + { + return -ENOMEM; + } + + req = bt_buf_add(buf, sizeof(*req)); + req->handle = BT_HOST2LE16(handle); + memcpy(req->value, data, length); + bt_buf_add(buf, length); + + wlinfo("handle 0x%04x length %u\n", handle, length); + + return gatt_send(conn, buf, att_write_rsp, func, NULL); +} + +void bt_gatt_cancel(FAR struct bt_conn_s *conn) +{ + bt_att_cancel(conn); +} + +int bt_gatt_read_multiple(FAR struct bt_conn_s *conn, + FAR const uint16_t * handles, size_t count, + bt_gatt_read_func_t func) +{ + FAR struct bt_buf_s *buf; + uint8_t i; + + if (!conn && conn->state != BT_CONN_CONNECTED) + { + return -ENOTCONN; + } + + if (!handles || count < 2 || !func) + { + return -EINVAL; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_REQ, + count * sizeof(*handles)); + if (!buf) + { + return -ENOMEM; + } + + for (i = 0; i < count; i++) + { + bt_buf_add_le16(buf, handles[i]); + } + + return gatt_send(conn, buf, att_read_rsp, func, NULL); +} diff --git a/wireless/bluetooth/bt_hcicore.c b/wireless/bluetooth/bt_hcicore.c new file mode 100644 index 00000000000..d3cf4309eb6 --- /dev/null +++ b/wireless/bluetooth/bt_hcicore.c @@ -0,0 +1,1669 @@ +/**************************************************************************** + * wireless/bluetooth/bt_att.c + * HCI core Bluetooth handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "bt_queue.h" +#include "bt_hcicore.h" +#include "bt_keys.h" +#include "bt_conn.h" +#include "bt_l2cap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum bt_stackdir_e +{ + STACK_DIRECTION_UP, + STACK_DIRECTION_DOWN, +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct bt_dev_s g_btdev; +static FAR struct bt_conn_cb_s *g_callback_list; +static bt_le_scan_cb_t *g_scan_dev_found_cb; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void bt_connected(FAR struct bt_conn_s *conn) +{ + FAR struct bt_conn_cb_s *cb; + + for (cb = g_callback_list; cb; cb = cb->next) + { + if (cb->connected) + { + cb->connected(conn); + } + } +} + +static void bt_disconnected(FAR struct bt_conn_s *conn) +{ + FAR struct bt_conn_cb_s *cb; + + for (cb = g_callback_list; cb; cb = cb->next) + { + if (cb->disconnected) + { + cb->disconnected(conn); + } + } +} + +void bt_conn_cb_register(FAR struct bt_conn_cb_s *cb) +{ + cb->next = g_callback_list; + g_callback_list = cb; +} + +FAR struct bt_buf_s *bt_hci_cmd_create(uint16_t opcode, uint8_t param_len) +{ + FAR struct bt_hci_cmd_hdr_s *hdr; + FAR struct bt_buf_s *buf; + + wlinfo("opcode %x param_len %u\n", opcode, param_len); + + buf = bt_buf_get(BT_CMD, g_btdev.dev->head_reserve); + if (!buf) + { + wlerr("ERROR: Cannot get free buffer\n"); + return NULL; + } + + wlinfo("buf %p\n", buf); + + buf->u.hci.opcode = opcode; + buf->u.hci.sync = NULL; + + hdr = bt_buf_add(buf, sizeof(*hdr)); + hdr->opcode = BT_HOST2LE16(opcode); + hdr->param_len = param_len; + + return buf; +} + +int bt_hci_cmd_send(uint16_t opcode, FAR struct bt_buf_s *buf) +{ + int ret; + if (!buf) + { + buf = bt_hci_cmd_create(opcode, 0); + if (!buf) + { + return -ENOBUFS; + } + } + + wlinfo("opcode %x len %u\n", opcode, buf->len); + + /* Host Number of Completed Packets can ignore the ncmd value and does not + * generate any cmd complete/status events. + */ + + if (opcode == BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS) + { + g_btdev.dev->send(buf); + bt_buf_put(buf); + return 0; + } + + ret = bt_queue_send(g_btdev.tx_queue, buf, BT_NORMAL_PRIO); + if (ret < 0) + { + wlerr("ERROR: bt_queue_send() failed: %d\n", ret); + } + + return ret; +} + +int bt_hci_cmd_send_sync(uint16_t opcode, FAR struct bt_buf_s *buf, + FAR struct bt_buf_s **rsp) +{ + sem_t sync_sem; + int ret; + + /* NOTE: This function cannot be called from the rx thread since it relies + * on the very same thread in processing the cmd_complete event and giving + * back the blocking semaphore. + */ + + if (!buf) + { + buf = bt_hci_cmd_create(opcode, 0); + if (!buf) + { + return -ENOBUFS; + } + } + + wlinfo("opcode %x len %u\n", opcode, buf->len); + + nxsem_init(&sync_sem, 0, 0); + nxsem_setprotocol(&sync_sem, SEM_PRIO_NONE); + buf->u.hci.sync = &sync_sem; + + ret = bt_queue_send(g_btdev.tx_queue, buf, BT_NORMAL_PRIO); + if (ret < 0) + { + wlerr("ERROR: bt_queue_send() failed: %d\n", ret); + } + + if (ret >= 0) + { + do + { + ret = nxsem_wait(&sync_sem); + } + while (ret == -EINTR); + } + + /* Indicate failure if we failed to get the return parameters */ + + if (ret >= 0) + { + if (!buf->u.hci.sync) + { + ret = -EIO; + } + else + { + ret = 0; + } + } + + if (rsp) + { + *rsp = buf->u.hci.sync; + } + else if (buf->u.hci.sync) + { + bt_buf_put(buf->u.hci.sync); + } + + bt_buf_put(buf); + return ret; +} + +static void hci_acl(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_acl_hdr_s *hdr = (FAR void *)buf->data; + FAR struct bt_conn_s *conn; + uint16_t handle; + uint16_t len = BT_LE162HOST(hdr->len); + uint8_t flags; + + wlinfo("buf %p\n", buf); + + handle = BT_LE162HOST(hdr->handle); + flags = (handle >> 12); + buf->u.acl.handle = bt_acl_handle(handle); + + bt_buf_pull(buf, sizeof(*hdr)); + + wlinfo("handle %u len %u flags %u\n", buf->u.acl.handle, len, flags); + + if (buf->len != len) + { + wlerr("ERROR: ACL data length mismatch (%u != %u)\n", buf->len, len); + bt_buf_put(buf); + return; + } + + conn = bt_conn_lookup_handle(buf->u.acl.handle); + if (!conn) + { + wlerr("ERROR: Unable to find conn for handle %u\n", buf->u.acl.handle); + bt_buf_put(buf); + return; + } + + bt_conn_recv(conn, buf, flags); + bt_conn_put(conn); +} + +/* HCI event processing */ + +static void hci_encrypt_change(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_encrypt_change_s *evt = (FAR void *)buf->data; + FAR struct bt_conn_s *conn; + uint16_t handle = BT_LE162HOST(evt->handle); + + wlinfo("status %u handle %u encrypt 0x%02x\n", + evt->status, handle, evt->encrypt); + + if (evt->status) + { + return; + } + + conn = bt_conn_lookup_handle(handle); + if (!conn) + { + wlerr("ERROR: Unable to look up conn with handle %u\n", handle); + return; + } + + conn->encrypt = evt->encrypt; + + bt_l2cap_encrypt_change(conn); + bt_conn_put(conn); +} + +static void hci_reset_complete(FAR struct bt_buf_s *buf) +{ + uint8_t status = buf->data[0]; + + wlinfo("status %u\n", status); + + if (status) + { + return; + } + + g_scan_dev_found_cb = NULL; + g_btdev.scan_enable = BT_LE_SCAN_DISABLE; + g_btdev.scan_filter = BT_LE_SCAN_FILTER_DUP_ENABLE; +} + +static void hci_cmd_done(uint16_t opcode, uint8_t status, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_buf_s *sent = g_btdev.sent_cmd; + + if (!sent) + { + return; + } + + if (g_btdev.sent_cmd->u.hci.opcode != opcode) + { + wlerr("ERROR: Unexpected completion of opcode 0x%04x\n", opcode); + return; + } + + g_btdev.sent_cmd = NULL; + + /* If the command was synchronous wake up bt_hci_cmd_send_sync() */ + + if (sent->u.hci.sync) + { + FAR sem_t *sem = sent->u.hci.sync; + + if (status) + { + sent->u.hci.sync = NULL; + } + else + { + sent->u.hci.sync = bt_buf_hold(buf); + } + + nxsem_post(sem); + } + else + { + bt_buf_put(sent); + } +} + +static void hci_cmd_complete(FAR struct bt_buf_s *buf) +{ + FAR struct hci_evt_cmd_complete_s *evt = (FAR void *)buf->data; + uint16_t opcode = BT_LE162HOST(evt->opcode); + FAR uint8_t *status; + + wlinfo("opcode %x\n", opcode); + + bt_buf_pull(buf, sizeof(*evt)); + + /* All command return parameters have a 1-byte status in the beginning, so we + * can safely make this generalization. + */ + + status = buf->data; + + switch (opcode) + { + case BT_HCI_OP_RESET: + hci_reset_complete(buf); + break; + + default: + wlinfo("Unhandled opcode %x\n", opcode); + break; + } + + hci_cmd_done(opcode, *status, buf); + + if (evt->ncmd && !g_btdev.ncmd) + { + /* Allow next command to be sent */ + + g_btdev.ncmd = 1; + nxsem_post(&g_btdev.ncmd_sem); + } +} + +static void hci_cmd_status(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_cmd_status_s *evt = (FAR void *)buf->data; + uint16_t opcode = BT_LE162HOST(evt->opcode); + + wlinfo("opcode %x\n", opcode); + + bt_buf_pull(buf, sizeof(*evt)); + + switch (opcode) + { + default: + wlinfo("Unhandled opcode %x\n", opcode); + break; + } + + hci_cmd_done(opcode, evt->status, buf); + + if (evt->ncmd && !g_btdev.ncmd) + { + /* Allow next command to be sent */ + + g_btdev.ncmd = 1; + nxsem_post(&g_btdev.ncmd_sem); + } +} + +static void hci_num_completed_packets(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_num_completed_packets_s *evt = (FAR void *)buf->data; + uint16_t i, num_handles = BT_LE162HOST(evt->num_handles); + + wlinfo("num_handles %u\n", num_handles); + + for (i = 0; i < num_handles; i++) + { + uint16_t handle, count; + + handle = BT_LE162HOST(evt->h[i].handle); + count = BT_LE162HOST(evt->h[i].count); + + wlinfo("handle %u count %u\n", handle, count); + + while (count--) + { + sem_post(&g_btdev.le_pkts_sem); + } + } +} + +static void hci_encrypt_key_refresh_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_encrypt_key_refresh_complete_s *evt = (FAR void *)buf->data; + FAR struct bt_conn_s *conn; + uint16_t handle; + + handle = BT_LE162HOST(evt->handle); + + wlinfo("status %u handle %u\n", evt->status, handle); + + if (evt->status) + { + return; + } + + conn = bt_conn_lookup_handle(handle); + if (!conn) + { + wlerr("ERROR: Unable to look up conn with handle %u\n", handle); + return; + } + + bt_l2cap_encrypt_change(conn); + bt_conn_put(conn); +} + +static void copy_id_addr(FAR struct bt_conn_s *conn, + FAR const bt_addr_le_t *addr) +{ + FAR struct bt_keys_s *keys; + + /* If we have a keys struct we already know the identity */ + + if (conn->keys) + { + return; + } + + keys = bt_keys_find_irk(addr); + if (keys) + { + bt_addr_le_copy(&conn->dst, &keys->addr); + conn->keys = keys; + } + else + { + bt_addr_le_copy(&conn->dst, addr); + } +} + +static int bt_hci_start_scanning(uint8_t scan_type, uint8_t scan_filter) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_buf_s *rsp; + FAR struct bt_hci_cp_le_set_scan_params_s *set_param; + FAR struct bt_hci_cp_le_set_scan_enable_s *scan_enable; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAMS, sizeof(*set_param)); + if (!buf) + { + return -ENOBUFS; + } + + set_param = bt_buf_add(buf, sizeof(*set_param)); + memset(set_param, 0, sizeof(*set_param)); + set_param->scan_type = scan_type; + + /* for the rest parameters apply default values according to spec 4.2, vol2, + * part E, 7.8.10 + */ + + set_param->interval = BT_HOST2LE16(0x0010); + set_param->window = BT_HOST2LE16(0x0010); + set_param->filter_policy = 0x00; + set_param->addr_type = 0x00; + + bt_hci_cmd_send(BT_HCI_OP_LE_SET_SCAN_PARAMS, buf); + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, sizeof(*scan_enable)); + if (!buf) + { + return -ENOBUFS; + } + + scan_enable = bt_buf_add(buf, sizeof(*scan_enable)); + memset(scan_enable, 0, sizeof(*scan_enable)); + scan_enable->filter_dup = scan_filter; + scan_enable->enable = BT_LE_SCAN_ENABLE; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, &rsp); + if (err) + { + return err; + } + + /* Update scan state in case of success (0) status */ + + err = rsp->data[0]; + if (!err) + { + g_btdev.scan_enable = BT_LE_SCAN_ENABLE; + } + + bt_buf_put(rsp); + return err; +} + +static int bt_hci_stop_scanning(void) +{ + FAR struct bt_buf_s *buf, *rsp; + FAR struct bt_hci_cp_le_set_scan_enable_s *scan_enable; + int err; + + if (g_btdev.scan_enable == BT_LE_SCAN_DISABLE) + { + return -EALREADY; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, + sizeof(*scan_enable)); + if (!buf) + { + return -ENOBUFS; + } + + scan_enable = bt_buf_add(buf, sizeof(*scan_enable)); + memset(scan_enable, 0x0, sizeof(*scan_enable)); + scan_enable->filter_dup = 0x00; + scan_enable->enable = BT_LE_SCAN_DISABLE; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, &rsp); + if (err) + { + return err; + } + + /* Update scan state in case of success (0) status */ + + err = rsp->data[0]; + if (!err) + { + g_btdev.scan_enable = BT_LE_SCAN_DISABLE; + } + + bt_buf_put(rsp); + return err; +} + +static int hci_le_create_conn(FAR const bt_addr_le_t *addr) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_hci_cp_le_create_conn_s *cp; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN, sizeof(*cp)); + if (!buf) + { + return -ENOBUFS; + } + + cp = bt_buf_add(buf, sizeof(*cp)); + memset(cp, 0x0, sizeof(*cp)); + bt_addr_le_copy(&cp->peer_addr, addr); + cp->conn_interval_max = BT_HOST2LE16(0x0028); + cp->conn_interval_min = BT_HOST2LE16(0x0018); + cp->scan_interval = BT_HOST2LE16(0x0060); + cp->scan_window = BT_HOST2LE16(0x0030); + cp->supervision_timeout = BT_HOST2LE16(0x07D0); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL); +} + +static void hci_disconn_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_disconn_complete_s *evt = (FAR void *)buf->data; + uint16_t handle = BT_LE162HOST(evt->handle); + FAR struct bt_conn_s *conn; + + wlinfo("status %u handle %u reason %u\n", evt->status, handle, evt->reason); + + if (evt->status) + { + return; + } + + conn = bt_conn_lookup_handle(handle); + if (!conn) + { + wlerr("ERROR: Unable to look up conn with handle %u\n", handle); + return; + } + + bt_l2cap_disconnected(conn); + bt_disconnected(conn); + + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + conn->handle = 0; + + if (bt_atomic_testbit(conn->flags, BT_CONN_AUTO_CONNECT)) + { + bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); + bt_le_scan_update(); + } + + bt_conn_put(conn); + + if (g_btdev.adv_enable) + { + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1); + if (buf) + { + memcpy(bt_buf_add(buf, 1), &g_btdev.adv_enable, 1); + bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_ENABLE, buf); + } + } +} + +static void le_conn_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_le_conn_complete_s *evt = (FAR void *)buf->data; + uint16_t handle = BT_LE162HOST(evt->handle); + FAR struct bt_conn_s *conn; + FAR struct bt_keys_s *keys; + + wlinfo("status %u handle %u role %u %s\n", evt->status, handle, + evt->role, bt_addr_le_str(&evt->peer_addr)); + + /* Make lookup to check if there's a connection object in CONNECT state + * associated with passed peer LE address. + */ + + keys = bt_keys_find_irk(&evt->peer_addr); + if (keys) + { + conn = bt_conn_lookup_state(&keys->addr, BT_CONN_CONNECT); + } + else + { + conn = bt_conn_lookup_state(&evt->peer_addr, BT_CONN_CONNECT); + } + + if (evt->status) + { + if (!conn) + { + return; + } + + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + + /* Drop the reference got by lookup call in CONNECT state. We are now in + * DISCONNECTED state since no successful LE link been made. + */ + + bt_conn_put(conn); + return; + } + + if (!conn) + { + conn = bt_conn_add(&evt->peer_addr, evt->role); + } + + if (!conn) + { + wlerr("ERROR: Unable to add new conn for handle %u\n", handle); + return; + } + + conn->handle = handle; + conn->src.type = BT_ADDR_LE_PUBLIC; + memcpy(conn->src.val, g_btdev.bdaddr.val, sizeof(g_btdev.bdaddr.val)); + copy_id_addr(conn, &evt->peer_addr); + conn->le_conn_interval = BT_LE162HOST(evt->interval); + + bt_conn_set_state(conn, BT_CONN_CONNECTED); + + bt_l2cap_connected(conn); + + if (evt->role == BT_HCI_ROLE_SLAVE) + { + bt_l2cap_update_conn_param(conn); + } + + bt_connected(conn); + bt_conn_put(conn); + bt_le_scan_update(); +} + +static void check_pending_conn(FAR const bt_addr_le_t *addr, uint8_t evtype, + FAR struct bt_keys_s *keys) +{ + FAR struct bt_conn_s *conn; + + /* Return if event is not connectible */ + + if (evtype != BT_LE_ADV_IND && evtype != BT_LE_ADV_DIRECT_IND) + { + return; + } + + if (keys) + { + conn = bt_conn_lookup_state(&keys->addr, BT_CONN_CONNECT_SCAN); + } + else + { + conn = bt_conn_lookup_state(addr, BT_CONN_CONNECT_SCAN); + } + + if (!conn) + { + return; + } + + if (bt_hci_stop_scanning()) + { + goto done; + } + + if (hci_le_create_conn(addr)) + { + goto done; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT); + +done: + bt_conn_put(conn); +} + +static void le_adv_report(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_ev_le_advertising_info_s *info; + uint8_t num_reports = buf->data[0]; + + wlinfo("Adv number of reports %u\n", num_reports); + + info = bt_buf_pull(buf, sizeof(num_reports)); + + while (num_reports--) + { + int8_t rssi = info->data[info->length]; + FAR struct bt_keys_s *keys; + bt_addr_le_t addr; + + wlinfo("%s event %u, len %u, rssi %d dBm\n", + bt_addr_le_str(&info->addr), info->evt_type, info->length, + rssi); + + keys = bt_keys_find_irk(&info->addr); + if (keys) + { + bt_addr_le_copy(&addr, &keys->addr); + wlinfo("Identity %s matched RPA %s\n", + bt_addr_le_str(&keys->addr), bt_addr_le_str(&info->addr)); + } + else + { + bt_addr_le_copy(&addr, &info->addr); + } + + if (g_scan_dev_found_cb) + { + g_scan_dev_found_cb(&addr, rssi, info->evt_type, + info->data, info->length); + } + + check_pending_conn(&info->addr, info->evt_type, keys); + + /* Get next report iteration by moving pointer to right offset in buf + * according to spec 4.2, Vol 2, Part E, 7.7.65.2. + */ + + info = bt_buf_pull(buf, sizeof(*info) + info->length + sizeof(rssi)); + } +} + +static void le_ltk_request(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_le_ltk_request_s *evt = (FAR void *)buf->data; + FAR struct bt_conn_s *conn; + uint16_t handle; + + handle = BT_LE162HOST(evt->handle); + + wlinfo("handle %u\n", handle); + + conn = bt_conn_lookup_handle(handle); + if (!conn) + { + wlerr("ERROR: Unable to lookup conn for handle %u\n", handle); + return; + } + + if (!conn->keys) + { + conn->keys = bt_keys_find(BT_KEYS_SLAVE_LTK, &conn->dst); + } + + if (conn->keys && (conn->keys->keys & BT_KEYS_SLAVE_LTK) && + conn->keys->slave_ltk.rand == evt->rand && + conn->keys->slave_ltk.ediv == evt->ediv) + { + FAR struct bt_hci_cp_le_ltk_req_reply_s *cp; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_REPLY, sizeof(*cp)); + if (!buf) + { + wlerr("ERROR: Out of command buffers\n"); + goto done; + } + + cp = bt_buf_add(buf, sizeof(*cp)); + cp->handle = evt->handle; + memcpy(cp->ltk, conn->keys->slave_ltk.val, 16); + + bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf); + } + else + { + FAR struct bt_hci_cp_le_ltk_req_neg_reply_s *cp; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, sizeof(*cp)); + if (!buf) + { + wlerr("ERROR: Out of command buffers\n"); + goto done; + } + + cp = bt_buf_add(buf, sizeof(*cp)); + cp->handle = evt->handle; + + bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, buf); + } + +done: + bt_conn_put(conn); +} + +static void hci_le_meta_event(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_le_meta_event_s *evt = (FAR void *)buf->data; + + bt_buf_pull(buf, sizeof(*evt)); + + switch (evt->subevent) + { + case BT_HCI_EVT_LE_CONN_COMPLETE: + le_conn_complete(buf); + break; + + case BT_HCI_EVT_LE_ADVERTISING_REPORT: + le_adv_report(buf); + break; + + case BT_HCI_EVT_LE_LTK_REQUEST: + le_ltk_request(buf); + break; + + default: + wlinfo("Unhandled LE event %x\n", evt->subevent); + break; + } +} + +static void hci_event(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_hdr_s *hdr = (FAR void *)buf->data; + + wlinfo("event %u\n", hdr->evt); + + bt_buf_pull(buf, sizeof(*hdr)); + + switch (hdr->evt) + { + case BT_HCI_EVT_DISCONN_COMPLETE: + hci_disconn_complete(buf); + break; + + case BT_HCI_EVT_ENCRYPT_CHANGE: + hci_encrypt_change(buf); + break; + + case BT_HCI_EVT_CMD_COMPLETE: + hci_cmd_complete(buf); + break; + + case BT_HCI_EVT_CMD_STATUS: + hci_cmd_status(buf); + break; + + case BT_HCI_EVT_NUM_COMPLETED_PACKETS: + hci_num_completed_packets(buf); + break; + + case BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE: + hci_encrypt_key_refresh_complete(buf); + break; + + case BT_HCI_EVT_LE_META_EVENT: + hci_le_meta_event(buf); + break; + + default: + wlwarn("Unhandled event 0x%02x\n", hdr->evt); + break; + } + + bt_buf_put(buf); +} + +static int hci_tx_kthread(int argc, FAR char *argv[]) +{ + FAR struct bt_driver_s *dev = g_btdev.dev; + int ret; + + wlinfo("started\n"); + + while (1) + { + FAR struct bt_buf_s *buf; + + /* Wait until ncmd > 0 */ + + do + { + ret = nxsem_wait(&g_btdev.ncmd_sem); + } + while (ret == -EINTR); + + DEBUGASSERT(ret >= 0); + + /* Get next command - wait if necessary */ + + buf = NULL; + ret = bt_queue_recv(g_btdev.tx_queue, &buf); + DEBUGASSERT(ret >= 0 && buf != NULL); + UNUSED(ret); + + g_btdev.ncmd = 0; + + wlinfo("Sending command %x (buf %p) to driver\n", + buf->u.hci.opcode, buf); + + dev->send(buf); + + /* Clear out any existing sent command */ + + if (g_btdev.sent_cmd) + { + wlerr("ERROR: Uncleared pending sent_cmd\n"); + bt_buf_put(g_btdev.sent_cmd); + g_btdev.sent_cmd = NULL; + } + + g_btdev.sent_cmd = buf; + } + + return EXIT_SUCCESS; /* Can't get here */ +} + +static int hci_rx_kthread(int argc, FAR char *argv[]) +{ + FAR struct bt_buf_s *buf; + int ret; + + wlinfo("started\n"); + + while (1) + { + ret = bt_queue_recv(g_btdev.rx_queue, &buf); + if (ret < 0) + { + wlerr("ERROR: bt_queue_recv() failed: %d\n", ret); + continue; + } + + wlinfo("buf %p type %u len %u\n", buf, buf->type, buf->len); + + switch (buf->type) + { + case BT_ACL_IN: + hci_acl(buf); + break; + + case BT_EVT: + hci_event(buf); + break; + + default: + wlerr("ERROR: Unknown buf type %u\n", buf->type); + bt_buf_put(buf); + break; + } + } + + return EXIT_SUCCESS; /* Can't get here */ +} + +static void read_local_features_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_rp_read_local_features_s *rp = (FAR void *)buf->data; + + wlinfo("status %u\n", rp->status); + + memcpy(g_btdev.features, rp->features, sizeof(g_btdev.features)); +} + +static void read_local_ver_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_rp_read_local_version_info_s *rp = (FAR void *)buf->data; + + wlinfo("status %u\n", rp->status); + + g_btdev.hci_version = rp->hci_version; + g_btdev.hci_revision = BT_LE162HOST(rp->hci_revision); + g_btdev.manufacturer = BT_LE162HOST(rp->manufacturer); +} + +static void read_bdaddr_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_rp_read_bd_addr_s *rp = (FAR void *)buf->data; + + wlinfo("status %u\n", rp->status); + + bt_addr_copy(&g_btdev.bdaddr, &rp->bdaddr); +} + +static void read_le_features_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_rp_le_read_local_features_s *rp = (FAR void *)buf->data; + + wlinfo("status %u\n", rp->status); + + memcpy(g_btdev.le_features, rp->features, sizeof(g_btdev.le_features)); +} + +static void read_buffer_size_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_rp_read_buffer_size_s *rp = (FAR void *)buf->data; + + wlinfo("status %u\n", rp->status); + + /* If LE-side has buffers we can ignore the BR/EDR values */ + + if (g_btdev.le_mtu) + { + return; + } + + g_btdev.le_mtu = BT_LE162HOST(rp->acl_max_len); + g_btdev.le_pkts = BT_LE162HOST(rp->acl_max_num); +} + +static void le_read_buffer_size_complete(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_rp_le_read_buffer_size_s *rp = (FAR void *)buf->data; + + wlinfo("status %u\n", rp->status); + + g_btdev.le_mtu = BT_LE162HOST(rp->le_max_len); + g_btdev.le_pkts = rp->le_max_num; +} + +static int hci_init(void) +{ + FAR struct bt_hci_cp_host_buffer_size_s *hbs; + FAR struct bt_hci_cp_set_event_mask_s *ev; + FAR struct bt_buf_s *buf; + FAR struct bt_buf_s *rsp; + FAR uint8_t *enable; + int err; + + /* Send HCI_RESET */ + + bt_hci_cmd_send(BT_HCI_OP_RESET, NULL); + + /* Read Local Supported Features */ + + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_FEATURES, NULL, &rsp); + if (err) + { + return err; + } + + read_local_features_complete(rsp); + bt_buf_put(rsp); + + /* Read Local Version Information */ + + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_VERSION_INFO, NULL, &rsp); + if (err) + { + return err; + } + + read_local_ver_complete(rsp); + bt_buf_put(rsp); + + /* Read Bluetooth Address */ + + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BD_ADDR, NULL, &rsp); + if (err) + { + return err; + } + + read_bdaddr_complete(rsp); + bt_buf_put(rsp); + + /* For now we only support LE capable controllers */ + + if (!lmp_le_capable(g_btdev)) + { + wlerr("ERROR: Non-LE capable controller detected!\n"); + return -ENODEV; + } + + /* Read Low Energy Supported Features */ + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL, &rsp); + if (err) + { + return err; + } + + read_le_features_complete(rsp); + bt_buf_put(rsp); + + /* Read LE Buffer Size */ + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE, NULL, &rsp); + if (err) + { + return err; + } + + le_read_buffer_size_complete(rsp); + bt_buf_put(rsp); + + buf = bt_hci_cmd_create(BT_HCI_OP_SET_EVENT_MASK, sizeof(*ev)); + if (!buf) + { + return -ENOBUFS; + } + + ev = bt_buf_add(buf, sizeof(*ev)); + memset(ev, 0, sizeof(*ev)); + ev->events[0] |= 0x10; /* Disconnection Complete */ + ev->events[1] |= 0x08; /* Read Remote Version Information Complete */ + ev->events[1] |= 0x20; /* Command Complete */ + ev->events[1] |= 0x40; /* Command Status */ + ev->events[1] |= 0x80; /* Hardware Error */ + ev->events[2] |= 0x04; /* Number of Completed Packets */ + ev->events[3] |= 0x02; /* Data Buffer Overflow */ + ev->events[7] |= 0x20; /* LE Meta-Event */ + + if (g_btdev.le_features[0] & BT_HCI_LE_ENCRYPTION) + { + ev->events[0] |= 0x80; /* Encryption Change */ + ev->events[5] |= 0x80; /* Encryption Key Refresh Complete */ + } + + bt_hci_cmd_send_sync(BT_HCI_OP_SET_EVENT_MASK, buf, NULL); + + buf = bt_hci_cmd_create(BT_HCI_OP_HOST_BUFFER_SIZE, sizeof(*hbs)); + if (!buf) + { + return -ENOBUFS; + } + + hbs = bt_buf_add(buf, sizeof(*hbs)); + memset(hbs, 0, sizeof(*hbs)); + hbs->acl_mtu = BT_HOST2LE16(BT_BUF_MAX_DATA - + sizeof(struct bt_hci_acl_hdr_s) - + g_btdev.dev->head_reserve); + hbs->acl_pkts = BT_HOST2LE16(CONFIG_BLUETOOTH_RXTHREAD_NMSGS); + + err = bt_hci_cmd_send(BT_HCI_OP_HOST_BUFFER_SIZE, buf); + if (err) + { + return err; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, 1); + if (!buf) + { + return -ENOBUFS; + } + + enable = bt_buf_add(buf, sizeof(*enable)); + *enable = 0x01; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, buf, NULL); + if (err) + { + return err; + } + + if (lmp_bredr_capable(g_btdev)) + { + struct bt_hci_cp_write_le_host_supp_s *cp; + + /* Use BR/EDR buffer size if LE reports zero buffers */ + + if (!g_btdev.le_mtu) + { + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE, NULL, &rsp); + if (err) + { + return err; + } + + read_buffer_size_complete(rsp); + bt_buf_put(rsp); + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, sizeof(*cp)); + if (!buf) + { + return -ENOBUFS; + } + + /* Explicitly enable LE for dual-mode controllers */ + + cp = bt_buf_add(buf, sizeof *cp); + cp->le = 0x01; + cp->simul = 0x00; + + bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf, NULL); + } + + wlinfo("HCI ver %u rev %u, manufacturer %u\n", g_btdev.hci_version, + g_btdev.hci_revision, g_btdev.manufacturer); + wlinfo("ACL buffers: pkts %u mtu %u\n", g_btdev.le_pkts, g_btdev.le_mtu); + + /* Initialize & prime the semaphore for counting controller-side available + * ACL packet buffers. + */ + + nxsem_init(&g_btdev.le_pkts_sem, 0, g_btdev.le_pkts); + return 0; +} + +/* threads, fifos and semaphores initialization */ + +static void cmd_queue_init(void) +{ + pid_t pid; + int ret; + + /* When there is a command to be sent to the Bluetooth driver, it queued on + * the Tx queue and received by logic on the Tx kernel thread. + */ + + g_btdev.tx_queue = NULL; + ret = bt_queue_open(BT_HCI_RX, O_RDWR | O_CREAT, + CONFIG_BLUETOOTH_RXTHREAD_NMSGS, &g_btdev.tx_queue); + DEBUGASSERT(ret >= 0 && g_btdev.tx_queue != NULL); + UNUSED(ret); + + nxsem_init(&g_btdev.ncmd_sem, 0, 1); + nxsem_setprotocol(&g_btdev.ncmd_sem, SEM_PRIO_NONE); + + g_btdev.ncmd = 1; + pid = kthread_create("BT HCI Tx", CONFIG_BLUETOOTH_TXCMD_PRIORITY, + CONFIG_BLUETOOTH_TXCMD_STACKSIZE, + hci_tx_kthread, NULL); + DEBUGASSERT(pid > 0); + UNUSED(pid); +} + +static void rx_queue_init(void) +{ + pid_t pid; + int ret; + + /* When a buffer is received from the Bluetooth driver via bt_recv() on + * the Rx queue and received by logic on the Rx kernel thread. + */ + + g_btdev.rx_queue = NULL; + ret = bt_queue_open(BT_HCI_RX, O_RDWR | O_CREAT, + CONFIG_BLUETOOTH_TXCMD_NMSGS, &g_btdev.rx_queue); + DEBUGASSERT(ret >= 0 && g_btdev.rx_queue != NULL); + UNUSED(ret); + + pid = kthread_create("BT HCI Rx", CONFIG_BLUETOOTH_RXTHREAD_PRIORITY, + CONFIG_BLUETOOTH_RXTHREAD_STACKSIZE, + hci_rx_kthread, NULL); + DEBUGASSERT(pid > 0); + UNUSED(pid); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Interface to HCI driver layer */ + +void bt_recv(FAR struct bt_buf_s *buf) +{ + FAR struct bt_hci_evt_hdr_s *hdr; + int prio = BT_NORMAL_PRIO; + int ret; + + wlinfo("buf %p len %u\n", buf, buf->len); + + if (buf->type != BT_ACL_IN) + { + if (buf->type != BT_EVT) + { + wlerr("ERROR: Invalid buf type %u\n", buf->type); + bt_buf_put(buf); + return; + } + + /* Command Complete/Status events use high priority messages. */ + + hdr = (FAR void *)buf->data; + if (hdr->evt == BT_HCI_EVT_CMD_COMPLETE || + hdr->evt == BT_HCI_EVT_CMD_STATUS || + hdr->evt == BT_HCI_EVT_NUM_COMPLETED_PACKETS) + { + prio = BT_HIGH_PRIO; + } + } + + ret = bt_queue_send(g_btdev.rx_queue, buf, prio); + if (ret < 0) + { + wlerr("ERROR: bt_queue_send() failed: %d\n", ret); + } +} + +int bt_driver_register(FAR struct bt_driver_s *dev) +{ + if (g_btdev.dev) + { + return -EALREADY; + } + + if (!dev->open || !dev->send) + { + return -EINVAL; + } + + g_btdev.dev = dev; + return 0; +} + +void bt_driver_unregister(FAR struct bt_driver_s *dev) +{ + g_btdev.dev = NULL; +} + +int bt_init(void) +{ + FAR struct bt_driver_s *dev = g_btdev.dev; + int err; + + if (!dev) + { + wlerr("ERROR: No HCI driver registered\n"); + return -ENODEV; + } + + bt_buf_init(); + + cmd_queue_init(); + rx_queue_init(); + + err = dev->open(); + if (err) + { + wlerr("ERROR: HCI driver open failed (%d)\n", err); + return err; + } + + err = hci_init(); + if (err) + { + return err; + } + + return bt_l2cap_init(); +} + +int bt_start_advertising(uint8_t type, FAR const struct bt_eir_s *ad, + FAR const struct bt_eir_s *sd) +{ + FAR struct bt_buf_s *buf; + FAR struct bt_hci_cp_le_set_adv_data_s *set_data; + FAR struct bt_hci_cp_le_set_adv_data_s *scan_rsp; + FAR struct bt_hci_cp_le_set_adv_parameters_s *set_param; + int i; + + if (!ad) + { + goto send_scan_rsp; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_DATA, sizeof(*set_data)); + if (!buf) + { + return -ENOBUFS; + } + + set_data = bt_buf_add(buf, sizeof(*set_data)); + + memset(set_data, 0, sizeof(*set_data)); + + for (i = 0; ad[i].len; i++) + { + /* Check if ad fit in the remaining buffer */ + + if (set_data->len + ad[i].len + 1 > 29) + { + break; + } + + memcpy(&set_data->data[set_data->len], &ad[i], ad[i].len + 1); + set_data->len += ad[i].len + 1; + } + + bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_DATA, buf); + +send_scan_rsp: + if (!sd) + { + goto send_set_param; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, + sizeof(*scan_rsp)); + if (!buf) + { + return -ENOBUFS; + } + + scan_rsp = bt_buf_add(buf, sizeof(*scan_rsp)); + + memset(scan_rsp, 0, sizeof(*scan_rsp)); + + for (i = 0; sd[i].len; i++) + { + /* Check if ad fit in the remaining buffer */ + + if (scan_rsp->len + sd[i].len + 1 > 29) + { + break; + } + + memcpy(&scan_rsp->data[scan_rsp->len], &sd[i], sd[i].len + 1); + scan_rsp->len += sd[i].len + 1; + } + + bt_hci_cmd_send(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, buf); + +send_set_param: + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAMETERS, + sizeof(*set_param)); + if (!buf) + { + return -ENOBUFS; + } + + set_param = bt_buf_add(buf, sizeof(*set_param)); + + memset(set_param, 0, sizeof(*set_param)); + set_param->min_interval = BT_HOST2LE16(0x0800); + set_param->max_interval = BT_HOST2LE16(0x0800); + set_param->type = type; + set_param->channel_map = 0x07; + + bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_PARAMETERS, buf); + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1); + if (!buf) + { + return -ENOBUFS; + } + + g_btdev.adv_enable = 0x01; + memcpy(bt_buf_add(buf, 1), &g_btdev.adv_enable, 1); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL); +} + +int bt_stop_advertising(void) +{ + FAR struct bt_buf_s *buf; + + if (!g_btdev.adv_enable) + { + return -EALREADY; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1); + if (!buf) + { + return -ENOBUFS; + } + + g_btdev.adv_enable = 0x00; + memcpy(bt_buf_add(buf, 1), &g_btdev.adv_enable, 1); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL); +} + +int bt_start_scanning(uint8_t scan_filter, bt_le_scan_cb_t cb) +{ + /* Return if active scan is already enabled */ + + if (g_scan_dev_found_cb) + { + return -EALREADY; + } + + g_scan_dev_found_cb = cb; + g_btdev.scan_filter = scan_filter; + + return bt_le_scan_update(); +} + +int bt_stop_scanning(void) +{ + /* Return if active scanning is already disabled */ + + if (!g_scan_dev_found_cb) + { + return -EALREADY; + } + + g_scan_dev_found_cb = NULL; + g_btdev.scan_filter = BT_LE_SCAN_FILTER_DUP_ENABLE; + + return bt_le_scan_update(); +} + +/* Used to determine whether to start scan and which scan type should be used */ + +int bt_le_scan_update(void) +{ + FAR struct bt_conn_s *conn; + + if (g_btdev.scan_enable) + { + int err; + + if (g_scan_dev_found_cb) + { + return 0; + } + + err = bt_hci_stop_scanning(); + if (err) + { + return err; + } + } + + if (g_scan_dev_found_cb) + { + return bt_hci_start_scanning(BT_LE_SCAN_ACTIVE, g_btdev.scan_filter); + } + + conn = bt_conn_lookup_state(BT_ADDR_LE_ANY, BT_CONN_CONNECT_SCAN); + if (!conn) + { + return 0; + } + + bt_conn_put(conn); + + return bt_hci_start_scanning(BT_LE_SCAN_PASSIVE, g_btdev.scan_filter); +} + +#ifdef CONFIG_DEBUG_WIRELESS_INFO +FAR const char *bt_addr_str(FAR const bt_addr_t *addr) +{ + static char bufs[2][18]; + static uint8_t cur; + FAR char *str; + + str = bufs[cur++]; + cur %= ARRAY_SIZE(bufs); + bt_addr_to_str(addr, str, sizeof(bufs[cur])); + + return str; +} + +FAR const char *bt_addr_le_str(FAR const bt_addr_le_t *addr) +{ + static char bufs[2][27]; + static uint8_t cur; + FAR char *str; + + str = bufs[cur++]; + cur %= ARRAY_SIZE(bufs); + bt_addr_le_to_str(addr, str, sizeof(bufs[cur])); + + return str; +} +#endif /* CONFIG_DEBUG_WIRELESS_INFO */ diff --git a/wireless/bluetooth/bt_hcicore.h b/wireless/bluetooth/bt_hcicore.h new file mode 100644 index 00000000000..db0b0677902 --- /dev/null +++ b/wireless/bluetooth/bt_hcicore.h @@ -0,0 +1,223 @@ +/**************************************************************************** + * wireless/bluetooth/bt_att.c + * HCI core Bluetooth handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __WIRELESS_BLUETOOTH_BT_HDICORE_H +#define __WIRELESS_BLUETOOTH_BT_HDICORE_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Enabling debug increases stack size requirement considerably */ + +#if defined(CONFIG_DEBUG_WIRELESS_INFO) +# define BT_STACK_DEBUG_EXTRA 512 +#else +# define BT_STACK_DEBUG_EXTRA 0 +#endif + +#define BT_STACK(name, size) \ + char __stack name[(size) + BT_STACK_DEBUG_EXTRA] +#define BT_STACK_NOINIT(name, size) \ + char __noinit __stack name[(size) + BT_STACK_DEBUG_EXTRA] + +/* LMP feature helpers */ + +#define lmp_bredr_capable(dev) (!((dev).features[4] & BT_LMP_NO_BREDR)) +#define lmp_le_capable(dev) ((dev).features[4] & BT_LMP_LE) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* State tracking for the local Bluetooth controller */ + +struct bt_dev_s +{ + /* Local Bluetooth Device Address */ + + bt_addr_t bdaddr; + + /* Controller version & manufacturer information */ + + uint8_t hci_version; + uint16_t hci_revision; + uint16_t manufacturer; + + /* BR/EDR features page 0 */ + + uint8_t features[8]; + + /* LE features */ + + uint8_t le_features[8]; + + /* Advertising state */ + + uint8_t adv_enable; + + /* Scanning state */ + + uint8_t scan_enable; + uint8_t scan_filter; + + /* Controller buffer information */ + + uint8_t le_pkts; + uint16_t le_mtu; + sem_t le_pkts_sem; + + /* Number of commands controller can accept */ + + uint8_t ncmd; + sem_t ncmd_sem; + + /* Last sent HCI command */ + + FAR struct bt_buf_s *sent_cmd; + + /* Queue for incoming HCI events and ACL data */ + + mqd_t rx_queue; + + /* Queue for outgoing HCI commands */ + + mqd_t tx_queue; + + /* Registered HCI driver */ + + FAR struct bt_driver_s *dev; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern struct bt_dev_s g_btdev; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +static inline int bt_addr_cmp(FAR const bt_addr_t *a, FAR const bt_addr_t *b) +{ + return memcmp(a, b, sizeof(*a)); +} + +static inline int bt_addr_le_cmp(FAR const bt_addr_le_t *a, FAR const bt_addr_le_t *b) +{ + return memcmp(a, b, sizeof(*a)); +} + +static inline void bt_addr_copy(FAR bt_addr_t *dst, FAR const bt_addr_t *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + +static inline void bt_addr_le_copy(FAR bt_addr_le_t *dst, FAR const bt_addr_le_t *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + +static inline bool bt_addr_le_is_rpa(FAR const bt_addr_le_t *addr) +{ + if (addr->type != BT_ADDR_LE_RANDOM) + { + return false; + } + + if ((addr->val[5] & 0xc0) == 0x40) + { + return true; + } + + return false; +} + +static inline bool bt_addr_le_is_identity(FAR const bt_addr_le_t *addr) +{ + if (addr->type == BT_ADDR_LE_PUBLIC) + { + return true; + } + + /* Check for Random Static address type */ + + if ((addr->val[5] & 0xc0) == 0xc0) + { + return true; + } + + return false; +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +FAR struct bt_buf_s *bt_hci_cmd_create(uint16_t opcode, uint8_t param_len); +int bt_hci_cmd_send(uint16_t opcode, FAR struct bt_buf_s *buf); +int bt_hci_cmd_send_sync(uint16_t opcode, FAR struct bt_buf_s *buf, + FAR struct bt_buf_s **rsp); +int bt_le_scan_update(void); + +/* The helper is only safe to be called from internal kernel threads as it's + * not multi-threading safe + */ + +#ifdef CONFIG_DEBUG_WIRELESS_INFO +FAR const char *bt_addr_str(FAR const bt_addr_t *addr); +FAR const char *bt_addr_le_str(FAR const bt_addr_le_t *addr); +#endif + +#endif /* __WIRELESS_BLUETOOTH_BT_HDICORE_H */ diff --git a/wireless/bluetooth/bt_keys.c b/wireless/bluetooth/bt_keys.c new file mode 100644 index 00000000000..ecb5b4a0cb9 --- /dev/null +++ b/wireless/bluetooth/bt_keys.c @@ -0,0 +1,376 @@ +/**************************************************************************** + * wireless/bluetooth/bt_keys.c + * Bluetooth key handling + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "bt_hcicore.h" +#include "bt_smp.h" +#include "bt_keys.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define bt_keys_foreach(list, cur, member) \ + for (cur = list; *cur; cur = &(*cur)->member) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct bt_keys_s g_key_pool[CONFIG_BLUETOOTH_MAX_PAIRED]; + +static FAR struct bt_keys_s *g_ltks; +static FAR struct bt_keys_s *g_slave_ltks; +static FAR struct bt_keys_s *g_irks; +static FAR struct bt_keys_s *g_local_csrks; +static FAR struct bt_keys_s *g_remote_csrks; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +FAR struct bt_keys_s *bt_keys_get_addr(FAR const bt_addr_le_t *addr) +{ + FAR struct bt_keys_s *keys; + int i; + + wlinfo("%s\n", bt_addr_le_str(addr)); + + for (i = 0; i < CONFIG_BLUETOOTH_MAX_PAIRED; i++) + { + keys = &g_key_pool[i]; + + if (!bt_addr_le_cmp(&keys->addr, addr)) + { + return keys; + } + + if (!bt_addr_le_cmp(&keys->addr, BT_ADDR_LE_ANY)) + { + bt_addr_le_copy(&keys->addr, addr); + wlinfo("created %p for %s\n", keys, bt_addr_le_str(addr)); + return keys; + } + } + + wlinfo("unable to create keys for %s\n", bt_addr_le_str(addr)); + + return NULL; +} + +void bt_keys_clear(FAR struct bt_keys_s *keys, int type) +{ + FAR struct bt_keys_s **cur; + + wlinfo("keys for %s type %d\n", bt_addr_le_str(&keys->addr), type); + + if (((type & keys->keys) & BT_KEYS_SLAVE_LTK)) + { + bt_keys_foreach(&g_slave_ltks, cur, slave_ltk.next) + { + if (*cur == keys) + { + *cur = (*cur)->slave_ltk.next; + break; + } + } + + keys->keys &= ~BT_KEYS_SLAVE_LTK; + } + + if (((type & keys->keys) & BT_KEYS_LTK)) + { + bt_keys_foreach(&g_ltks, cur, ltk.next) + { + if (*cur == keys) + { + *cur = (*cur)->ltk.next; + break; + } + } + + keys->keys &= ~BT_KEYS_LTK; + } + + if (((type & keys->keys) & BT_KEYS_IRK)) + { + bt_keys_foreach(&g_irks, cur, irk.next) + { + if (*cur == keys) + { + *cur = (*cur)->irk.next; + break; + } + } + + keys->keys &= ~BT_KEYS_IRK; + } + + if (((type & keys->keys) & BT_KEYS_LOCAL_CSRK)) + { + bt_keys_foreach(&g_local_csrks, cur, local_csrk.next) + { + if (*cur == keys) + { + *cur = (*cur)->local_csrk.next; + break; + } + } + + keys->keys &= ~BT_KEYS_LOCAL_CSRK; + } + + if (((type & keys->keys) & BT_KEYS_REMOTE_CSRK)) + { + bt_keys_foreach(&g_remote_csrks, cur, remote_csrk.next) + { + if (*cur == keys) + { + *cur = (*cur)->remote_csrk.next; + break; + } + } + + keys->keys &= ~BT_KEYS_REMOTE_CSRK; + } + + if (!keys->keys) + { + memset(keys, 0, sizeof(*keys)); + } +} + +FAR struct bt_keys_s *bt_keys_find(int type, FAR const bt_addr_le_t *addr) +{ + FAR struct bt_keys_s **cur; + + wlinfo("type %d %s\n", type, bt_addr_le_str(addr)); + + switch (type) + { + case BT_KEYS_SLAVE_LTK: + bt_keys_foreach(&g_slave_ltks, cur, slave_ltk.next) + { + if (!bt_addr_le_cmp(&(*cur)->addr, addr)) + { + break; + } + } + + return *cur; + + case BT_KEYS_LTK: + bt_keys_foreach(&g_ltks, cur, ltk.next) + { + if (!bt_addr_le_cmp(&(*cur)->addr, addr)) + { + break; + } + } + + return *cur; + + case BT_KEYS_IRK: + bt_keys_foreach(&g_irks, cur, irk.next) + { + if (!bt_addr_le_cmp(&(*cur)->addr, addr)) + { + break; + } + } + + return *cur; + + case BT_KEYS_LOCAL_CSRK: + bt_keys_foreach(&g_local_csrks, cur, local_csrk.next) + { + if (!bt_addr_le_cmp(&(*cur)->addr, addr)) + { + break; + } + } + + return *cur; + + case BT_KEYS_REMOTE_CSRK: + bt_keys_foreach(&g_remote_csrks, cur, remote_csrk.next) + { + if (!bt_addr_le_cmp(&(*cur)->addr, addr)) + { + break; + } + } + + return *cur; + + default: + return NULL; + } +} + +void bt_keys_add_type(FAR struct bt_keys_s *keys, int type) +{ + if ((keys->keys & type) != 0) + { + return; + } + + switch (type) + { + case BT_KEYS_SLAVE_LTK: + keys->slave_ltk.next = g_slave_ltks; + g_slave_ltks = keys; + break; + + case BT_KEYS_LTK: + keys->ltk.next = g_ltks; + g_ltks = keys; + break; + + case BT_KEYS_IRK: + keys->irk.next = g_irks; + g_irks = keys; + break; + + case BT_KEYS_LOCAL_CSRK: + keys->local_csrk.next = g_local_csrks; + g_local_csrks = keys; + break; + + case BT_KEYS_REMOTE_CSRK: + keys->remote_csrk.next = g_remote_csrks; + g_remote_csrks = keys; + break; + + default: + wlerr("ERROR: Unknown key type %d\n", type); + return; + } + + keys->keys |= type; +} + +FAR struct bt_keys_s *bt_keys_get_type(int type, + FAR const bt_addr_le_t *addr) +{ + FAR struct bt_keys_s *keys; + + wlinfo("type %d %s\n", type, bt_addr_le_str(addr)); + + keys = bt_keys_find(type, addr); + if (keys) + { + return keys; + } + + keys = bt_keys_get_addr(addr); + if (!keys) + { + return NULL; + } + + bt_keys_add_type(keys, type); + return keys; +} + +FAR struct bt_keys_s *bt_keys_find_irk(FAR const bt_addr_le_t * addr) +{ + FAR struct bt_keys_s **cur; + + wlinfo("%s\n", bt_addr_le_str(addr)); + + if (!bt_addr_le_is_rpa(addr)) + { + return NULL; + } + + bt_keys_foreach(&g_irks, cur, irk.next) + { + FAR struct bt_irk_s *irk = &(*cur)->irk; + + if (!bt_addr_cmp((bt_addr_t *) addr->val, &irk->rpa)) + { + wlinfo("cached RPA %s for %s\n", bt_addr_str(&irk->rpa), + bt_addr_le_str(&(*cur)->addr)); + return *cur; + } + + if (bt_smp_irk_matches(irk->val, (bt_addr_t *) addr->val)) + { + FAR struct bt_keys_s *match = *cur; + + wlinfo("RPA %s matches %s\n", bt_addr_str(&irk->rpa), + bt_addr_le_str(&(*cur)->addr)); + + bt_addr_copy(&irk->rpa, (bt_addr_t *) addr->val); + + /* Move to the beginning of the list for faster future lookups. */ + + if (match != g_irks) + { + /* Remove match from list */ + + *cur = irk->next; + + /* Add match to the beginning */ + + irk->next = g_irks; + g_irks = match; + } + + return match; + } + } + + wlinfo("No IRK for %s\n", bt_addr_le_str(addr)); + return NULL; +} diff --git a/wireless/bluetooth/bt_keys.h b/wireless/bluetooth/bt_keys.h new file mode 100644 index 00000000000..3014ca44da6 --- /dev/null +++ b/wireless/bluetooth/bt_keys.h @@ -0,0 +1,107 @@ +/**************************************************************************** + * wireless/bluetooth/bt_keys.h + * Bluetooth key handling + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum +{ + BT_KEYS_SLAVE_LTK = (1 << 0), + BT_KEYS_IRK = (1 << 1), + BT_KEYS_LTK = (1 << 2), + BT_KEYS_LOCAL_CSRK = (1 << 3), + BT_KEYS_REMOTE_CSRK = (1 << 4), + + BT_KEYS_ALL = (BT_KEYS_SLAVE_LTK | BT_KEYS_IRK | BT_KEYS_LTK | + BT_KEYS_LOCAL_CSRK | BT_KEYS_REMOTE_CSRK), +}; + +struct bt_ltk_s +{ + uint64_t rand; + uint16_t ediv; + uint8_t val[16]; + FAR struct bt_keys_s *next; +}; + +struct bt_irk_s +{ + uint8_t val[16]; + bt_addr_t rpa; + FAR struct bt_keys_s *next; +}; + +struct bt_csrk_s +{ + uint8_t val[16]; + uint32_t cnt; + FAR struct bt_keys_s *next; +}; + +struct bt_keys_s +{ + bt_addr_le_t addr; + int keys; + struct bt_ltk_s slave_ltk; + struct bt_ltk_s ltk; + struct bt_irk_s irk; + struct bt_csrk_s local_csrk; + struct bt_csrk_s remote_csrk; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +FAR struct bt_keys_s *bt_keys_get_addr(FAR const bt_addr_le_t *addr); +FAR struct bt_keys_s *bt_keys_get_type(int type, + FAR const bt_addr_le_t *addr); +void bt_keys_add_type(FAR struct bt_keys_s *keys, int type); +void bt_keys_clear(FAR struct bt_keys_s *keys, int type); +FAR struct bt_keys_s *bt_keys_find(int type, FAR const bt_addr_le_t *addr); +FAR struct bt_keys_s *bt_keys_find_irk(FAR const bt_addr_le_t *addr); diff --git a/wireless/bluetooth/bt_l2cap.c b/wireless/bluetooth/bt_l2cap.c new file mode 100644 index 00000000000..2176e93d8d3 --- /dev/null +++ b/wireless/bluetooth/bt_l2cap.c @@ -0,0 +1,425 @@ +/**************************************************************************** + * wireless/bluetooth/bt_l2cap.c + * L2CAP handling + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "bt_hcicore.h" +#include "bt_conn.h" +#include "bt_l2cap.h" +#include "bt_att.h" +#include "bt_smp.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define LE_CONN_MIN_INTERVAL 0x0028 +#define LE_CONN_MAX_INTERVAL 0x0038 +#define LE_CONN_LATENCY 0x0000 +#define LE_CONN_TIMEOUT 0x002a + +#define BT_L2CAP_CONN_PARAM_ACCEPTED 0 +#define BT_L2CAP_CONN_PARAM_REJECTED 1 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR struct bt_l2cap_chan_s *g_channels; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint8_t get_ident(FAR struct bt_conn_s *conn) +{ + conn->l2cap.ident++; + + /* Handle integer overflow (0 is not valid) */ + + if (!conn->l2cap.ident) + { + conn->l2cap.ident++; + } + + return conn->l2cap.ident; +} + +void bt_l2cap_chan_register(FAR struct bt_l2cap_chan_s *chan) +{ + wlinfo("CID 0x%04x\n", chan->cid); + + chan->next = g_channels; + g_channels = chan; +} + +void bt_l2cap_connected(FAR struct bt_conn_s *conn) +{ + FAR struct bt_l2cap_chan_s *chan; + + for (chan = g_channels; chan; chan = chan->next) + { + if (chan->connected) + { + chan->connected(conn); + } + } +} + +void bt_l2cap_disconnected(FAR struct bt_conn_s *conn) +{ + FAR struct bt_l2cap_chan_s *chan; + + for (chan = g_channels; chan; chan = chan->next) + { + if (chan->disconnected) + { + chan->disconnected(conn); + } + } +} + +void bt_l2cap_encrypt_change(FAR struct bt_conn_s *conn) +{ + FAR struct bt_l2cap_chan_s *chan; + + for (chan = g_channels; chan; chan = chan->next) + { + if (chan->encrypt_change) + { + chan->encrypt_change(conn); + } + } +} + +struct bt_buf_s *bt_l2cap_create_pdu(FAR struct bt_conn_s *conn) +{ + size_t head_reserve = sizeof(struct bt_l2cap_hdr_s) + + sizeof(struct bt_hci_acl_hdr_s) + g_btdev.dev->head_reserve; + + return bt_buf_get(BT_ACL_OUT, head_reserve); +} + +void bt_l2cap_send(FAR struct bt_conn_s *conn, uint16_t cid, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_l2cap_hdr_s *hdr; + + hdr = bt_buf_push(buf, sizeof(*hdr)); + hdr->len = BT_HOST2LE16(buf->len - sizeof(*hdr)); + hdr->cid = BT_HOST2LE16(cid); + + bt_conn_send(conn, buf); +} + +static void rej_not_understood(FAR struct bt_conn_s *conn, uint8_t ident) +{ + FAR struct bt_l2cap_cmd_reject_s *rej; + FAR struct bt_l2cap_sig_hdr_s *hdr; + FAR struct bt_buf_s *buf; + + buf = bt_l2cap_create_pdu(conn); + if (!buf) + { + return; + } + + hdr = bt_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CMD_REJECT; + hdr->ident = ident; + hdr->len = BT_HOST2LE16(sizeof(*rej)); + + rej = bt_buf_add(buf, sizeof(*rej)); + rej->reason = BT_HOST2LE16(BT_L2CAP_REJ_NOT_UNDERSTOOD); + + bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); +} + +static void le_conn_param_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + struct bt_l2cap_conn_param_rsp_s *rsp = (void *)buf->data; + + if (buf->len < sizeof(*rsp)) + { + wlerr("ERROR: Too small LE conn param rsp\n"); + return; + } + + wlinfo("LE conn param rsp result %u\n", BT_LE162HOST(rsp->result)); +} + +static uint16_t le_validate_conn_params(uint16_t min, uint16_t max, + uint16_t latency, uint16_t timeout) +{ + uint16_t max_latency; + + if (min > max || min < 6 || max > 3200) + { + return BT_L2CAP_CONN_PARAM_REJECTED; + } + + if (timeout < 10 || timeout > 3200) + { + return BT_L2CAP_CONN_PARAM_REJECTED; + } + + /* Calculation based on BT spec 4.2 [Vol3, PartA, 4.20] max_latency = + * ((timeout * 10)/(max * 1.25 * 2)) - 1; + */ + + max_latency = (timeout * 4 / max) - 1; + if (latency > 499 || latency > max_latency) + { + return BT_L2CAP_CONN_PARAM_REJECTED; + } + + return BT_L2CAP_CONN_PARAM_ACCEPTED; +} + +static void le_conn_param_update_req(FAR struct bt_conn_s *conn, + uint8_t ident, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_l2cap_sig_hdr_s *hdr; + FAR struct bt_l2cap_conn_param_rsp_s *rsp; + FAR struct bt_l2cap_conn_param_req_s *req = (void *)buf->data; + uint16_t min; + uint16_t max; + uint16_t latency; + uint16_t timeout; + uint16_t result; + + if (buf->len < sizeof(*req)) + { + wlerr("ERROR: Too small LE conn update param req\n"); + return; + } + + if (conn->role != BT_HCI_ROLE_MASTER) + { + return; + } + + min = BT_LE162HOST(req->min_interval); + max = BT_LE162HOST(req->max_interval); + latency = BT_LE162HOST(req->latency); + timeout = BT_LE162HOST(req->timeout); + + wlinfo("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x timeout: 0x%4.4x", + min, max, latency, timeout); + + buf = bt_l2cap_create_pdu(conn); + if (!buf) + { + return; + } + + result = le_validate_conn_params(min, max, latency, timeout); + + hdr = bt_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CONN_PARAM_RSP; + hdr->ident = ident; + hdr->len = BT_HOST2LE16(sizeof(*rsp)); + + rsp = bt_buf_add(buf, sizeof(*rsp)); + memset(rsp, 0, sizeof(*rsp)); + rsp->result = BT_HOST2LE16(result); + + bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); + + if (result == BT_L2CAP_CONN_PARAM_ACCEPTED) + { + bt_conn_le_conn_update(conn, min, max, latency, timeout); + } +} + +static void le_sig(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf) +{ + struct bt_l2cap_sig_hdr_s *hdr = (FAR void *)buf->data; + uint16_t len; + + if (buf->len < sizeof(*hdr)) + { + wlerr("ERROR: Too small L2CAP LE signaling PDU\n"); + goto drop; + } + + len = BT_LE162HOST(hdr->len); + bt_buf_pull(buf, sizeof(*hdr)); + + wlinfo("LE signaling code 0x%02x ident %u len %u\n", hdr->code, + hdr->ident, len); + + if (buf->len != len) + { + wlerr("ERROR: L2CAP length mismatch (%u != %u)\n", buf->len, len); + goto drop; + } + + if (!hdr->ident) + { + wlerr("ERROR: Invalid ident value in L2CAP PDU\n"); + goto drop; + } + + switch (hdr->code) + { + case BT_L2CAP_CONN_PARAM_RSP: + le_conn_param_rsp(conn, buf); + break; + + case BT_L2CAP_CONN_PARAM_REQ: + le_conn_param_update_req(conn, hdr->ident, buf); + break; + + default: + wlwarn("Unknown L2CAP PDU code 0x%02x\n", hdr->code); + rej_not_understood(conn, hdr->ident); + break; + } + +drop: + bt_buf_put(buf); +} + +void bt_l2cap_recv(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf) +{ + FAR struct bt_l2cap_hdr_s *hdr = (FAR void *)buf->data; + FAR struct bt_l2cap_chan_s *chan; + uint16_t cid; + + if (buf->len < sizeof(*hdr)) + { + wlerr("ERROR: Too small L2CAP PDU received\n"); + bt_buf_put(buf); + return; + } + + cid = BT_LE162HOST(hdr->cid); + bt_buf_pull(buf, sizeof(*hdr)); + + wlinfo("Packet for CID %u len %u\n", cid, buf->len); + + for (chan = g_channels; chan; chan = chan->next) + { + if (chan->cid == cid) + { + break; + } + } + + if (!chan) + { + wlwarn("Ignoring data for unknown CID 0x%04x\n", cid); + bt_buf_put(buf); + return; + } + + chan->recv(conn, buf); +} + +void bt_l2cap_update_conn_param(FAR struct bt_conn_s *conn) +{ + FAR struct bt_l2cap_sig_hdr_s *hdr; + FAR struct bt_l2cap_conn_param_req_s *req; + FAR struct bt_buf_s *buf; + + /* Check if we need to update anything */ + + if (conn->le_conn_interval >= LE_CONN_MIN_INTERVAL && + conn->le_conn_interval <= LE_CONN_MAX_INTERVAL) + { + return; + } + + buf = bt_l2cap_create_pdu(conn); + if (!buf) + { + return; + } + + hdr = bt_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CONN_PARAM_REQ; + hdr->ident = get_ident(conn); + hdr->len = BT_HOST2LE16(sizeof(*req)); + + req = bt_buf_add(buf, sizeof(*req)); + req->min_interval = BT_HOST2LE16(LE_CONN_MIN_INTERVAL); + req->max_interval = BT_HOST2LE16(LE_CONN_MAX_INTERVAL); + req->latency = BT_HOST2LE16(LE_CONN_LATENCY); + req->timeout = BT_HOST2LE16(LE_CONN_TIMEOUT); + + bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); +} + +int bt_l2cap_init(void) +{ + int err; + + static struct bt_l2cap_chan_s chan = + { + .cid = BT_L2CAP_CID_LE_SIG, + .recv = le_sig, + }; + + bt_att_init(); + + err = bt_smp_init(); + if (err) + { + return err; + } + + bt_l2cap_chan_register(&chan); + return 0; +} diff --git a/wireless/bluetooth/bt_l2cap.h b/wireless/bluetooth/bt_l2cap.h new file mode 100644 index 00000000000..a816c84c3b1 --- /dev/null +++ b/wireless/bluetooth/bt_l2cap.h @@ -0,0 +1,154 @@ +/**************************************************************************** + * wireless/bluetooth/bt_l2cap.c + * L2CAP handling + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __WIRELESS_BLUETOOTH_BT_L2CAP_H +#define __WIRELESS_BLUETOOTH_BT_L2CAP_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BT_L2CAP_CID_ATT 0x0004 +#define BT_L2CAP_CID_LE_SIG 0x0005 +#define BT_L2CAP_CID_SMP 0x0006 + +#define BT_L2CAP_REJ_NOT_UNDERSTOOD 0x0000 +#define BT_L2CAP_REJ_MTU_EXCEEDED 0x0001 +#define BT_L2CAP_REJ_INVALID_CID 0x0002 + +#define BT_L2CAP_CMD_REJECT 0x01 +#define BT_L2CAP_CONN_PARAM_REQ 0x12 +#define BT_L2CAP_CONN_PARAM_RSP 0x13 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +begin_packed_struct struct bt_l2cap_hdr_s +{ + uint16_t len; + uint16_t cid; +} end_packed_struct; + +begin_packed_struct struct bt_l2cap_sig_hdr_s +{ + uint8_t code; + uint8_t ident; + uint16_t len; +} end_packed_struct; + +begin_packed_struct struct bt_l2cap_cmd_reject_s +{ + uint16_t reason; + uint8_t data[0]; +} end_packed_struct; + +begin_packed_struct struct bt_l2cap_conn_param_req_s +{ + uint16_t min_interval; + uint16_t max_interval; + uint16_t latency; + uint16_t timeout; +} end_packed_struct; + +begin_packed_struct struct bt_l2cap_conn_param_rsp_s +{ + uint16_t result; +} end_packed_struct; + +struct bt_l2cap_chan_s +{ + uint16_t cid; + + CODE void (*connected)(FAR struct bt_conn_s *conn); + CODE void (*disconnected)(FAR struct bt_conn_s *conn); + CODE void (*encrypt_change)(FAR struct bt_conn_s *conn); + CODE void (*recv)(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf); + + FAR struct bt_l2cap_chan_s *next; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Register a fixed L2CAP channel for L2CAP */ + +void bt_l2cap_chan_register(FAR struct bt_l2cap_chan_s *chan); + +/* Notify L2CAP channels of a new connection */ + +void bt_l2cap_connected(FAR struct bt_conn_s *conn); + +/* Notify L2CAP channels of a disconnect event */ + +void bt_l2cap_disconnected(FAR struct bt_conn_s *conn); + +/* Notify L2CAP channels of a change in encryption state */ + +void bt_l2cap_encrypt_change(FAR struct bt_conn_s *conn); + +/* Prepare an L2CAP PDU to be sent over a connection */ + +FAR struct bt_buf_s *bt_l2cap_create_pdu(FAR struct bt_conn_s *conn); + +/* Send L2CAP PDU over a connection */ + +void bt_l2cap_send(FAR struct bt_conn_s *conn, uint16_t cid, + FAR struct bt_buf_s *buf); + +/* Receive a new L2CAP PDU from a connection */ + +void bt_l2cap_recv(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf); + +/* Perform connection parameter update request */ + +void bt_l2cap_update_conn_param(FAR struct bt_conn_s *conn); + +/* Initialize L2CAP and supported channels */ + +int bt_l2cap_init(void); + +#endif /* __WIRELESS_BLUETOOTH_BT_L2CAP_H */ diff --git a/wireless/bluetooth/bt_queue.c b/wireless/bluetooth/bt_queue.c new file mode 100644 index 00000000000..f807347a9dd --- /dev/null +++ b/wireless/bluetooth/bt_queue.c @@ -0,0 +1,213 @@ +/**************************************************************************** + * wireless/bluetooth/bt_queue.c + * Inter-thread buffer queue management + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "bt_queue.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Common essage queue attributes */ + +#define BT_MSGSIZE sizeof(struct bt_bufmsg_s) +#define BT_MSGFLAGS 0 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct bt_bufmsg_s +{ + FAR struct iob_s *iob; +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bt_queue_open + * + * Description: + * Open a message queue for read or write access. + * + * Input Parameters: + * name - The name of the message queue to open + * oflags - Open flags with access mode + * nmsgs - Max number of messages in queue before bt_queue_send() blocks. + * mqd - The location in which to return the message queue descriptor + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure; + * + ****************************************************************************/ + +int bt_queue_open(FAR const char *name, int oflags, int nmsgs, + FAR mqd_t *mqd) +{ + struct mq_attr attr; + mqd_t newmqd; + int ret = OK; + + /* Initialize the message queue attributes */ + + attr.mq_maxmsg = nmsgs; + attr.mq_msgsize = BT_MSGSIZE; + attr.mq_flags = BT_MSGFLAGS; + + newmqd = mq_open(name, oflags, 0666, &attr); + if (newmqd == (mqd_t)-1) + { + /* REVISIT: mq_open() modifies the errno value */ + + ret = -get_errno(); + gerr("ERROR: mq_open(%s) failed: %d\n", name, ret); + newmqd = NULL; + } + + *mqd = newmqd; + return ret; +} + +/**************************************************************************** + * Name: bt_queue_recv + * + * Description: + * Block until the next buffer is received on the queue. + * + * Input Parameters: + * mqd - The message queue descriptor previously returned by bt_open_*queue. + * buf - The location in which to return the received buffer. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure; + * + ****************************************************************************/ + +int bt_queue_recv(mqd_t mqd, FAR struct bt_buf_s **buf) +{ + union + { + struct bt_bufmsg_s msg; + char msgbuf[BT_MSGSIZE]; + } u; + + FAR struct bt_buf_s *retbuf; + ssize_t msgsize; + int priority; + int ret; + + DEBUGASSERT(mqd != NULL && buf != NULL); + + /* Wait for the next message */ + + msgsize = nxmq_receive(mqd, u.msgbuf, BT_MSGSIZE, &priority); + if (msgsize < 0) + { + wlerr("ERROR: nxmq_receive() failed: %d\n", ret); + return ret; + } + + /* Only message buffer messages are expected */ + + DEBUGASSERT(msgsize == sizeof(struct bt_bufmsg_s)); + DEBUGASSERT(u.msg.iob != NULL); + + /* A few more sanity checks, then return the buffer */ + + retbuf = (FAR struct bt_buf_s *)u.msg.iob->io_data; + DEBUGASSERT(retbuf->iob == u.msg.iob); + + *buf = retbuf; + return OK; +} + +/**************************************************************************** + * Name: bt_queue_send + * + * Description: + * Send the buffer to the specified message queue + * + * Input Parameters: + * mqd - The message queue descriptor previously returned by + * bt_open_*queue. + * buf - A reference to the buffer to be sent + * priority - Either BT_NORMAL_PRIO or BT_NORMAL_HIGH. NOTE: + * BT_NORMAL_HIGHis only for use within the stack. Drivers + * should always use BT_NORMAL_PRIO. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure; + * + ****************************************************************************/ + +int bt_queue_send(mqd_t mqd, FAR struct bt_buf_s *buf, int priority) +{ + struct bt_bufmsg_s msg; + int ret; + + DEBUGASSERT(mqd != NULL && buf != NULL && buf->iob != NULL); + + /* Format and send the buffer message */ + + msg.iob = buf->iob; + ret = nxmq_send(mqd, (FAR const char *)&msg, sizeof(struct bt_bufmsg_s), + priority); + if (ret < 0) + { + wlerr("ERROR: mq_send() failed: %d\n", ret); + } + + return ret; +} diff --git a/wireless/bluetooth/bt_queue.h b/wireless/bluetooth/bt_queue.h new file mode 100644 index 00000000000..3c4719c2d80 --- /dev/null +++ b/wireless/bluetooth/bt_queue.h @@ -0,0 +1,147 @@ +/**************************************************************************** + * wireless/bluetooth/bt_queue.h + * Inter-thread buffer queue management + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __WIRELESS_BLUETOOTH_BT_QUEUE_H +#define __WIRELESS_BLUETOOTH_BT_QUEUE_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Names of all POSIX message queues. */ + +#define BT_CONN_TX "btconntx" +#define BT_HCI_TX "bthcitx" +#define BT_HCI_RX "bthcitx" + +/* All messages are sent FIFO at the mid-message priorities except for high- + * priority messages received from the Bluetooth driver. + */ + +#define BT_PRIO_MAX MQ_PRIO_MAX +#define BT_PRIO_MIN 0 + +#define BT_NORMAL_PRIO (BT_PRIO_MIN + (BT_PRIO_MAX - BT_PRIO_MIN) / 2) +#define BT_HIGH_PRIO (BT_PRIO_MIN + 3 * (BT_PRIO_MAX - BT_PRIO_MIN) / 4) + +/* Verify that enough messages have been allocated */ + +#define BT_NMSGS (CONFIG_BLUETOOTH_RXTHREAD_NMSGS + \ + CONFIG_BLUETOOTH_TXCMD_NMSGS + \ + CONFIG_BLUETOOTH_TXCONN_NMSGS) + +#if BT_NMSGS > CONFIG_PREALLOC_MQ_MSGS +# warning WARNING: not enough pre-allocated messages +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +struct bt_buf_s; /* Forward Reference */ + +/**************************************************************************** + * Name: bt_queue_open + * + * Description: + * Open a message queue for read or write access. + * + * Input Parameters: + * name - The name of the message queue to open + * oflags - Open flags with access mode + * nmsgs - Max number of messages in queue before bt_queue_send() blocks. + * mqd - The location in which to return the message queue descriptor + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure; + * + ****************************************************************************/ + +int bt_queue_open(FAR const char *name, int oflags, int nmsgs, + FAR mqd_t *mqd); + +/**************************************************************************** + * Name: bt_queue_recv + * + * Description: + * Block until the next buffer is received on the queue. + * + * Input Parameters: + * mqd - The message queue descriptor previously returned by bt_open_*queue. + * buf - The location in which to return the received buffer. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure; + * + ****************************************************************************/ + +int bt_queue_recv(mqd_t mqd, FAR struct bt_buf_s **buf); + +/**************************************************************************** + * Name: bt_queue_send + * + * Description: + * Send the buffer to the specified message queue + * + * Input Parameters: + * mqd - The message queue descriptor previously returned by + * bt_open_*queue. + * buf - A reference to the buffer to be sent + * priority - Either BT_NORMAL_PRIO or BT_NORMAL_HIGH. NOTE: + * BT_NORMAL_HIGHis only for use within the stack. Drivers + * should always use BT_NORMAL_PRIO. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure; + * + ****************************************************************************/ + +int bt_queue_send(mqd_t mqd, FAR struct bt_buf_s *buf, int priority); + +#endif /* __WIRELESS_BLUETOOTH_BT_QUEUE_H */ diff --git a/wireless/bluetooth/bt_smp.c b/wireless/bluetooth/bt_smp.c new file mode 100644 index 00000000000..fa8e1187b21 --- /dev/null +++ b/wireless/bluetooth/bt_smp.c @@ -0,0 +1,1641 @@ +/**************************************************************************** + * wireless/bluetooth/bt_smp.c + * Security Manager Protocol implementation. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "bt_atomic.h" +#include "bt_hcicore.h" +#include "bt_keys.h" +#include "bt_conn.h" +#include "bt_l2cap.h" +#include "bt_smp.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define RECV_KEYS (BT_SMP_DIST_ID_KEY | BT_SMP_DIST_ENC_KEY) +#define SEND_KEYS (BT_SMP_DIST_ENC_KEY) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* SMP channel specific context */ + +struct bt_smp_s +{ + /* The connection this context is associated with */ + + FAR struct bt_conn_s *conn; + + /* Commands that remote is allowed to send */ + + bt_atomic_t allowed_cmds; + + /* If we're waiting for an encryption change event */ + + bool pending_encrypt; + + /* Pairing Request PDU */ + + uint8_t preq[7]; + + /* Pairing Response PDU */ + + uint8_t prsp[7]; + + /* Pairing Confirm PDU */ + + uint8_t pcnf[16]; + + /* Local random number */ + + uint8_t prnd[16]; + + /* Remote random number */ + + uint8_t rrnd[16]; + + /* Temporary key */ + + uint8_t tk[16]; + + /* Local key distribution */ + + uint8_t local_dist; + + /* Remote key distribution */ + + uint8_t remote_dist; +}; + +struct uint128_s +{ + uint64_t a; + uint64_t b; +}; + +struct bt_smphandlers_s +{ + CODE uint8_t(*func)(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf); + uint8_t expect_len; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + + #ifdef CONFIG_DEBUG_WIRELESS_INFO +static const char *h(FAR const void *buf, size_t len); +#endif +static void xor_128(FAR const struct uint128_s *p, + FAR const struct uint128_s *q, FAR struct uint128_s *r); +static int le_encrypt(const uint8_t key[16], const uint8_t plaintext[16], + uint8_t enc_data[16]); +static int le_rand(FAR void *buf, size_t len); +static int smp_ah(FAR const uint8_t irk[16], FAR const uint8_t r[3], + FAR uint8_t out[3]); +static int smp_c1(FAR const uint8_t k[16], FAR const uint8_t r[16], + FAR const uint8_t preq[7], FAR const uint8_t pres[7], + FAR const bt_addr_le_t *ia, FAR const bt_addr_le_t *ra, + FAR uint8_t enc_data[16]); +static int smp_s1(const uint8_t k[16], const uint8_t r1[16], + const uint8_t r2[16], uint8_t out[16]); +static FAR struct bt_buf_s *bt_smp_create_pdu(FAR struct bt_conn_s *conn, + uint8_t op, size_t len); +static void send_err_rsp(FAR struct bt_conn_s *conn, uint8_t reason); +static int smp_init(struct bt_smp_s *smp); +static uint8_t smp_pairing_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t smp_pairing_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t smp_send_pairing_random(FAR struct bt_conn_s *conn); +static uint8_t smp_pairing_confirm(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t smp_pairing_random(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t smp_pairing_failed(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static void bt_smp_distribute_keys(FAR struct bt_conn_s *conn); +static uint8_t smp_encrypt_info(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t smp_master_ident(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t smp_ident_info(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t smp_ident_addr_info(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static uint8_t smp_security_request(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static void bt_smp_recv(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf); +static void bt_smp_connected(FAR struct bt_conn_s *conn); +static void bt_smp_disconnected(FAR struct bt_conn_s *conn); +static void bt_smp_encrypt_change(FAR FAR struct bt_conn_s *conn); +#ifdef CONFIG_BLUETOOTH_SMP_SELFTEST +static void swap_buf(FAR const uint8_t *src, FAR uint8_t *dst, + uint16_t len); +static void swap_in_place(FAR uint8_t * buf, uint16_t len); +static int cmac_subkey(FAR const uint8_t *key, FAR uint8_t *k1, + FAR uint8_t *k2); +static void add_pad(FAR const uint8_t *in, FAR unsigned char *out, + int len); +static int bt_smp_aes_cmac(const uint8_t *key, const uint8_t *in, + size_t len, uint8_t *out); +static int aes_test(FAR const char *prefix, FAR const uint8_t *key, + FAR const uint8_t *m, uint16_t len, + FAR const uint8_t *mac); +static int smp_aes_cmac_test(void); +static int smp_self_test(void); +#else +# define smp_self_test() (0) +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct bt_smp_s g_smp_pool[CONFIG_BLUETOOTH_MAX_CONN]; +static const struct bt_smphandlers_s g_smp_handlers[] = +{ + { + /* No op-code defined for 0x00 */ + }, + { + smp_pairing_req, + sizeof(struct bt_smp_pairing_s) + }, + { + smp_pairing_rsp, + sizeof(struct bt_smp_pairing_s) + }, + { + smp_pairing_confirm, + sizeof(struct bt_smp_pairing_confirm_s) + }, + { + smp_pairing_random, + sizeof(struct bt_smp_pairing_random_s) + }, + { + smp_pairing_failed, + sizeof(struct bt_smp_pairing_fail_s) + }, + { + smp_encrypt_info, + sizeof(struct bt_smp_encrypt_info_s) + }, + { + smp_master_ident, + sizeof(struct bt_smp_master_ident_s) + }, + { + smp_ident_info, + sizeof(struct bt_smp_ident_info_s) + }, + { + smp_ident_addr_info, + sizeof(struct bt_smp_ident_addr_info_s) + }, + { + /* Signing Information - Not yet implemented */ + }, + { + smp_security_request, + sizeof(struct bt_smp_security_request_s) + } +}; + +#define NHANDLERS (sizeof(g_smp_handlers) / sizeof(struct bt_smphandlers_s)) + +#ifdef CONFIG_BLUETOOTH_SMP_SELFTEST +/* Test vectors are taken from RFC 4493 + * https://tools.ietf.org/html/rfc4493 + * Same mentioned in the Bluetooth Spec. + */ + +static const uint8_t g_key[] = +{ + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c +}; + +static const uint8_t g_M[] = +{ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 +}; + +static const uint8_t g_mac1[] = +{ + 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 +}; + +static const uint8_t g_mac2[] = +{ + 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c +}; + +static const uint8_t g_mac3[] = +{ + 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 +}; + +static const uint8_t g_mac4[] = +{ + 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Helper for syslog parameters to convert from binary to hex. + * We declare multiple buffers so the helper can be used multiple times + * in a single syslog call. + */ + +#ifdef CONFIG_DEBUG_WIRELESS_INFO +static const char *h(FAR const void *buf, size_t len) +{ + static const char hex[] = "0123456789abcdef"; + static char hexbufs[4][129]; + static uint8_t curbuf; + FAR const uint8_t *b = buf; + FAR char *str; + size_t maxlen; + int i; + + str = hexbufs[curbuf++]; + curbuf %= ARRAY_SIZE(hexbufs); + + maxlen = (sizeof(hexbufs[0]) - 1) / 2; + if (len > maxlen) + { + len = maxlen; + } + + for (i = 0; i < len; i++) + { + str[i * 2] = hex[b[i] >> 4]; + str[i * 2 + 1] = hex[b[i] & 0xf]; + } + + str[i * 2] = '\0'; + return str; +} +#endif + +static void xor_128(FAR const struct uint128_s *p, + FAR const struct uint128_s *q, + FAR struct uint128_s *r) +{ + r->a = p->a ^ q->a; + r->b = p->b ^ q->b; +} + +static int le_encrypt(const uint8_t key[16], const uint8_t plaintext[16], + uint8_t enc_data[16]) +{ + FAR struct bt_hci_cp_le_encrypt_s *cp; + FAR struct bt_hci_rp_le_encrypt_s *rp; + FAR struct bt_buf_s *buf, *rsp; + int err; + + wlinfo("key %s plaintext %s\n", h(key, 16), h(plaintext, 16)); + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_ENCRYPT, sizeof(*cp)); + if (!buf) + { + return -ENOBUFS; + } + + cp = bt_buf_add(buf, sizeof(*cp)); + memcpy(cp->key, key, sizeof(cp->key)); + memcpy(cp->plaintext, plaintext, sizeof(cp->plaintext)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ENCRYPT, buf, &rsp); + if (err) + { + return err; + } + + rp = (void *)rsp->data; + memcpy(enc_data, rp->enc_data, sizeof(rp->enc_data)); + bt_buf_put(rsp); + + wlinfo("enc_data %s\n", h(enc_data, 16)); + + return 0; +} + +static int le_rand(FAR void *buf, size_t len) +{ + FAR uint8_t *ptr = buf; + + while (len > 0) + { + FAR struct bt_hci_rp_le_rand_s *rp; + FAR struct bt_buf_s *rsp; + size_t copy; + int err; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp); + if (err) + { + wlerr("ERROR: HCI_LE_Random failed (%d)\n", err); + return err; + } + + rp = (FAR void *)rsp->data; + copy = len; + if (copy > sizeof(rp->rand)) + { + copy = sizeof(rp->rand); + } + + memcpy(ptr, rp->rand, copy); + bt_buf_put(rsp); + + len -= copy; + ptr += copy; + } + + return 0; +} + +static int smp_ah(FAR const uint8_t irk[16], FAR const uint8_t r[3], + FAR uint8_t out[3]) +{ + uint8_t res[16]; + int err; + + wlinfo("irk %s\n, r %s", h(irk, 16), h(r, 3)); + + /* r' = padding || r */ + + memcpy(res, r, 3); + memset(res + 3, 0, 13); + + err = le_encrypt(irk, res, res); + if (err) + { + return err; + } + + /* The output of the random address function ah is: ah(h, r) = e(k, r') mod + * 2^24 The output of the security function e is then truncated to 24 bits by + * taking the least significant 24 bits of the output of e as the result of + * ah. + */ + + memcpy(out, res, 3); + return 0; +} + +static int smp_c1(FAR const uint8_t k[16], FAR const uint8_t r[16], + FAR const uint8_t preq[7], FAR const uint8_t pres[7], + FAR const bt_addr_le_t *ia, FAR const bt_addr_le_t *ra, + FAR uint8_t enc_data[16]) +{ + uint8_t p1[16]; + uint8_t p2[16]; + int err; + + wlinfo("k %s r %s\n", h(k, 16), h(r, 16)); + wlinfo("ia %s ra %s\n", bt_addr_le_str(ia), bt_addr_le_str(ra)); + wlinfo("preq %s pres %s\n", h(preq, 7), h(pres, 7)); + + /* pres, preq, rat and iat are concatenated to generate p1 */ + + p1[0] = ia->type; + p1[1] = ra->type; + memcpy(p1 + 2, preq, 7); + memcpy(p1 + 9, pres, 7); + + wlinfo("p1 %s\n", h(p1, 16)); + + /* c1 = e(k, e(k, r XOR p1) XOR p2) */ + + /* Using enc_data as temporary output buffer */ + + xor_128((FAR struct uint128_s *)r, (FAR struct uint128_s *)p1, + (FAR struct uint128_s *)enc_data); + + err = le_encrypt(k, enc_data, enc_data); + if (err) + { + return err; + } + + /* ra is concatenated with ia and padding to generate p2 */ + + memcpy(p2, ra->val, 6); + memcpy(p2 + 6, ia->val, 6); + memset(p2 + 12, 0, 4); + + wlinfo("p2 %s\n", h(p2, 16)); + + xor_128((FAR struct uint128_s *)enc_data, (FAR struct uint128_s *)p2, (FAR struct uint128_s *)enc_data); + return le_encrypt(k, enc_data, enc_data); +} + +static int smp_s1(const uint8_t k[16], const uint8_t r1[16], + const uint8_t r2[16], uint8_t out[16]) +{ + /* The most significant 64-bits of r1 are discarded to generate r1' and the + * most significant 64-bits of r2 are discarded to generate r2'. r1' is + * concatenated with r2' to generate r' which is used as the 128-bit input + * parameter plaintextData to security function e: r' = r1' || r2' + */ + + memcpy(out, r2, 8); + memcpy(out + 8, r1, 8); + + /* s1(k, r1 , r2) = e(k, r') */ + + return le_encrypt(k, out, out); +} + +static FAR struct bt_buf_s *bt_smp_create_pdu(FAR struct bt_conn_s *conn, + uint8_t op, size_t len) +{ + FAR struct bt_smp_hdr_s *hdr; + FAR struct bt_buf_s *buf; + + buf = bt_l2cap_create_pdu(conn); + if (!buf) + { + return NULL; + } + + hdr = bt_buf_add(buf, sizeof(*hdr)); + hdr->code = op; + + return buf; +} + +static void send_err_rsp(FAR struct bt_conn_s *conn, uint8_t reason) +{ + FAR struct bt_smp_pairing_fail_s *rsp; + FAR struct bt_buf_s *buf; + + buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_FAIL, sizeof(*rsp)); + if (!buf) + { + return; + } + + rsp = bt_buf_add(buf, sizeof(*rsp)); + rsp->reason = reason; + + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf); +} + +static int smp_init(struct bt_smp_s *smp) +{ + /* Initialize SMP context */ + + memset(smp, 0, sizeof(*smp)); + + /* Generate local random number */ + + if (le_rand(smp->prnd, 16)) + { + return BT_SMP_ERR_UNSPECIFIED; + } + + wlinfo("prnd %s\n", h(smp->prnd, 16)); + + return 0; +} + +static uint8_t smp_pairing_req(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_smp_pairing_s *req = (void *)buf->data; + FAR struct bt_smp_pairing_s *rsp; + FAR struct bt_buf_s *rsp_buf; + FAR struct bt_smp_s *smp = conn->smp; + uint8_t auth; + int ret; + + wlinfo("\n"); + + if ((req->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) || + (req->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE)) + { + return BT_SMP_ERR_ENC_KEY_SIZE; + } + + ret = smp_init(smp); + if (ret) + { + return ret; + } + + rsp_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_RSP, sizeof(*rsp)); + if (!rsp_buf) + { + return BT_SMP_ERR_UNSPECIFIED; + } + + rsp = bt_buf_add(rsp_buf, sizeof(*rsp)); + + /* For JustWorks pairing simplify rsp parameters. TODO: needs to be reworked + * later on. + */ + + auth = (req->auth_req & BT_SMP_AUTH_MASK); + auth &= ~(BT_SMP_AUTH_MITM | BT_SMP_AUTH_SC | + BT_SMP_AUTH_KEYPRESS); + rsp->auth_req = auth; + rsp->io_capability = BT_SMP_IO_NO_INPUT_OUTPUT; + rsp->oob_flag = BT_SMP_OOB_NOT_PRESENT; + rsp->max_key_size = req->max_key_size; + rsp->init_key_dist = (req->init_key_dist & RECV_KEYS); + rsp->resp_key_dist = (req->resp_key_dist & SEND_KEYS); + + smp->local_dist = rsp->resp_key_dist; + smp->remote_dist = rsp->init_key_dist; + + memset(smp->tk, 0, sizeof(smp->tk)); + + /* Store req/rsp for later use */ + + smp->preq[0] = BT_SMP_CMD_PAIRING_REQ; + memcpy(smp->preq + 1, req, sizeof(*req)); + smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP; + memcpy(smp->prsp + 1, rsp, sizeof(*rsp)); + + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, rsp_buf); + + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + return 0; +} + +static uint8_t smp_send_pairing_confirm(FAR struct bt_conn_s *conn) +{ + FAR struct bt_smp_pairing_confirm_s *req; + FAR struct bt_smp_s *smp = conn->smp; + FAR const bt_addr_le_t *ra; + FAR const bt_addr_le_t *ia; + FAR struct bt_buf_s *rsp_buf; + int err; + + rsp_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_CONFIRM, sizeof(*req)); + if (!rsp_buf) + { + return BT_SMP_ERR_UNSPECIFIED; + } + + req = bt_buf_add(rsp_buf, sizeof(*req)); + + if (conn->role == BT_HCI_ROLE_MASTER) + { + ra = &conn->dst; + ia = &conn->src; + } + else + { + ra = &conn->src; + ia = &conn->dst; + } + + err = smp_c1(smp->tk, smp->prnd, smp->preq, smp->prsp, ia, ra, req->val); + if (err) + { + bt_buf_put(rsp_buf); + return BT_SMP_ERR_UNSPECIFIED; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, rsp_buf); + + return 0; +} + +static uint8_t smp_pairing_rsp(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + struct bt_smp_pairing_s *rsp = (void *)buf->data; + struct bt_smp_s *smp = conn->smp; + + wlinfo("\n"); + + if ((rsp->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) || + (rsp->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE)) + { + return BT_SMP_ERR_ENC_KEY_SIZE; + } + + smp->local_dist &= rsp->init_key_dist; + smp->remote_dist &= rsp->resp_key_dist; + + /* Store rsp for later use */ + smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP; + memcpy(smp->prsp + 1, rsp, sizeof(*rsp)); + + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + + return smp_send_pairing_confirm(conn); +} + +static uint8_t smp_send_pairing_random(FAR struct bt_conn_s *conn) +{ + FAR struct bt_smp_pairing_random_s *req; + FAR struct bt_buf_s *rsp_buf; + FAR struct bt_smp_s *smp = conn->smp; + + rsp_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_RANDOM, sizeof(*req)); + if (!rsp_buf) + { + return BT_SMP_ERR_UNSPECIFIED; + } + + req = bt_buf_add(rsp_buf, sizeof(*req)); + memcpy(req->val, smp->prnd, sizeof(req->val)); + + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, rsp_buf); + + return 0; +} + +static uint8_t smp_pairing_confirm(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + struct bt_smp_pairing_confirm_s *req = (void *)buf->data; + struct bt_smp_s *smp = conn->smp; + + wlinfo("\n"); + + memcpy(smp->pcnf, req->val, sizeof(smp->pcnf)); + + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + + if (conn->role == BT_HCI_ROLE_SLAVE) + { + return smp_send_pairing_confirm(conn); + } + + return smp_send_pairing_random(conn); +} + +static uint8_t smp_pairing_random(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_smp_pairing_random_s *req = (void *)buf->data; + FAR const bt_addr_le_t *ra; + FAR const bt_addr_le_t *ia; + FAR struct bt_smp_s *smp = conn->smp; + FAR struct bt_keys_s *keys; + uint8_t cfm[16]; + int err; + + wlinfo("\n"); + + memcpy(smp->rrnd, req->val, sizeof(smp->rrnd)); + + if (conn->role == BT_HCI_ROLE_MASTER) + { + ra = &conn->dst; + ia = &conn->src; + } + else + { + ra = &conn->src; + ia = &conn->dst; + } + + err = smp_c1(smp->tk, smp->rrnd, smp->preq, smp->prsp, ia, ra, cfm); + if (err) + { + return BT_SMP_ERR_UNSPECIFIED; + } + + wlinfo("pcnf %s cfm %s\n", h(smp->pcnf, 16), h(cfm, 16)); + + if (memcmp(smp->pcnf, cfm, sizeof(smp->pcnf))) + { + return BT_SMP_ERR_CONFIRM_FAILED; + } + + if (conn->role == BT_HCI_ROLE_MASTER) + { + uint8_t stk[16]; + + /* No need to store master STK */ + err = smp_s1(smp->tk, smp->rrnd, smp->prnd, stk); + if (err) + { + return BT_SMP_ERR_UNSPECIFIED; + } + + /* Rand and EDiv are 0 for the STK */ + if (bt_conn_le_start_encryption(conn, 0, 0, stk)) + { + wlerr("ERROR: Failed to start encryption\n"); + return BT_SMP_ERR_UNSPECIFIED; + } + + smp->pending_encrypt = true; + + return 0; + } + + keys = bt_keys_get_type(BT_KEYS_SLAVE_LTK, &conn->dst); + if (keys == NULL) + { + wlerr("ERROR: Unable to create new keys\n"); + return BT_SMP_ERR_UNSPECIFIED; + } + + err = smp_s1(smp->tk, smp->prnd, smp->rrnd, keys->slave_ltk.val); + if (err) + { + bt_keys_clear(keys, BT_KEYS_SLAVE_LTK); + return BT_SMP_ERR_UNSPECIFIED; + } + + /* Rand and EDiv are 0 for the STK */ + keys->slave_ltk.rand = 0; + keys->slave_ltk.ediv = 0; + + wlinfo("generated STK %s\n", h(keys->slave_ltk.val, 16)); + + smp->pending_encrypt = true; + + smp_send_pairing_random(conn); + + return 0; +} + +static uint8_t smp_pairing_failed(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + struct bt_smp_pairing_fail_s *req = (void *)buf->data; + struct bt_smp_s *smp = conn->smp; + + wlerr("ERROR: reason 0x%x\n", req->reason); + UNUSED(req); + + bt_atomic_set(&smp->allowed_cmds, 0); + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL); + + if (conn->role == BT_HCI_ROLE_MASTER) + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST); + } + else + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ); + } + + /* return no error to avoid sending Pairing Failed in response */ + return 0; +} + +static void bt_smp_distribute_keys(FAR struct bt_conn_s *conn) +{ + FAR struct bt_smp_s *smp = conn->smp; + FAR struct bt_keys_s *keys; + FAR struct bt_buf_s *buf; + + keys = bt_keys_get_addr(&conn->dst); + if (!keys) + { + wlerr("ERROR: Unable to look up keys for %s\n", bt_addr_le_str(&conn->dst)); + return; + } + + if (!smp->local_dist) + { + bt_keys_clear(keys, BT_KEYS_ALL); + return; + } + + if (smp->local_dist & BT_SMP_DIST_ENC_KEY) + { + struct bt_smp_encrypt_info_s *info; + struct bt_smp_master_ident_s *ident; + + bt_keys_add_type(keys, BT_KEYS_SLAVE_LTK); + + le_rand(keys->slave_ltk.val, sizeof(keys->slave_ltk.val)); + le_rand(&keys->slave_ltk.rand, sizeof(keys->slave_ltk.rand)); + le_rand(&keys->slave_ltk.ediv, sizeof(keys->slave_ltk.ediv)); + + buf = bt_smp_create_pdu(conn, BT_SMP_CMD_ENCRYPT_INFO, + sizeof(struct bt_smp_encrypt_info_s)); + if (!buf) + { + wlerr("ERROR: Unable to allocate Encrypt Info buffer\n"); + return; + } + + info = bt_buf_add(buf, sizeof(struct bt_smp_encrypt_info_s)); + memcpy(info->ltk, keys->slave_ltk.val, sizeof(info->ltk)); + + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf); + + buf = bt_smp_create_pdu(conn, BT_SMP_CMD_MASTER_IDENT, + sizeof(struct bt_smp_master_ident_s)); + if (!buf) + { + wlerr("ERROR: Unable to allocate Master Ident buffer\n"); + return; + } + + ident = bt_buf_add(buf, sizeof(struct bt_smp_master_ident_s)); + ident->rand = keys->slave_ltk.rand; + ident->ediv = keys->slave_ltk.ediv; + + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf); + } +} + +static uint8_t smp_encrypt_info(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_smp_encrypt_info_s *req = (void *)buf->data; + FAR struct bt_smp_s *smp = conn->smp; + FAR struct bt_keys_s *keys; + + wlinfo("\n"); + + keys = bt_keys_get_type(BT_KEYS_LTK, &conn->dst); + if (!keys) + { + wlerr("ERROR: Unable to get keys for %s\n", bt_addr_le_str(&conn->dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(keys->ltk.val, req->ltk, 16); + + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_MASTER_IDENT); + + return 0; +} + +static uint8_t smp_master_ident(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_smp_master_ident_s *req = (void *)buf->data; + FAR struct bt_smp_s *smp = conn->smp; + FAR struct bt_keys_s *keys; + + wlinfo("\n"); + + keys = bt_keys_get_type(BT_KEYS_LTK, &conn->dst); + if (!keys) + { + wlerr("ERROR: Unable to get keys for %s\n", bt_addr_le_str(&conn->dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + keys->ltk.ediv = req->ediv; + keys->ltk.rand = req->rand; + + if (conn->role == BT_HCI_ROLE_MASTER) + { + smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY; + if (!smp->remote_dist) + { + bt_smp_distribute_keys(conn); + + return 0; + } + } + + if (smp->remote_dist & BT_SMP_DIST_ID_KEY) + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO); + } + + return 0; +} + +static uint8_t smp_ident_info(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_smp_ident_info_s *req = (FAR void *)buf->data; + FAR struct bt_smp_s *smp = conn->smp; + FAR struct bt_keys_s *keys; + + wlinfo("\n"); + + keys = bt_keys_get_type(BT_KEYS_IRK, &conn->dst); + if (!keys) + { + wlerr("ERROR: Unable to get keys for %s\n", bt_addr_le_str(&conn->dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(keys->irk.val, req->irk, 16); + + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_ADDR_INFO); + + return 0; +} + +static uint8_t smp_ident_addr_info(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_smp_ident_addr_info_s *req = (void *)buf->data; + FAR struct bt_smp_s *smp = conn->smp; + FAR struct bt_keys_s *keys; + + wlinfo("identity %s\n", bt_addr_le_str(&req->addr)); + + if (!bt_addr_le_is_identity(&req->addr)) + { + wlerr("ERROR: Invalid identity %s for %s\n", + bt_addr_le_str(&req->addr), bt_addr_le_str(&conn->dst)); + return BT_SMP_ERR_INVALID_PARAMS; + } + + keys = bt_keys_get_type(BT_KEYS_IRK, &conn->dst); + if (!keys) + { + wlerr("ERROR: Unable to get keys for %s\n", bt_addr_le_str(&conn->dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + if (bt_addr_le_is_rpa(&conn->dst)) + { + bt_addr_copy(&keys->irk.rpa, (bt_addr_t *) & conn->dst.val); + bt_addr_le_copy(&keys->addr, &req->addr); + bt_addr_le_copy(&conn->dst, &req->addr); + } + + if (conn->role == BT_HCI_ROLE_MASTER) + { + smp->remote_dist &= ~BT_SMP_DIST_ID_KEY; + if (!smp->remote_dist) + { + bt_smp_distribute_keys(conn); + } + } + + return 0; +} + +static uint8_t smp_security_request(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_smp_security_request_s *req = (FAR void *)buf->data; + FAR struct bt_keys_s *keys; + uint8_t auth; + + wlinfo("\n"); + + keys = bt_keys_find(BT_KEYS_LTK, &conn->dst); + if (!keys) + { + goto pair; + } + + auth = req->auth_req & BT_SMP_AUTH_MASK; + if (auth & (BT_SMP_AUTH_MITM | BT_SMP_AUTH_SC)) + { + wlwarn("Unsupported auth requirements: 0x%x, repairing", auth); + goto pair; + } + + if (bt_conn_le_start_encryption(conn, keys->ltk.rand, keys->ltk.ediv, + keys->ltk.val) < 0) + { + return BT_SMP_ERR_UNSPECIFIED; + } + + return 0; +pair: + if (bt_smp_send_pairing_req(conn) < 0) + { + return BT_SMP_ERR_UNSPECIFIED; + } + + return 0; +} + +static void bt_smp_recv(FAR struct bt_conn_s *conn, + FAR struct bt_buf_s *buf) +{ + FAR struct bt_smp_hdr_s *hdr = (FAR void *)buf->data; + FAR struct bt_smp_s *smp = conn->smp; + uint8_t err; + + if (buf->len < sizeof(*hdr)) + { + wlerr("ERROR: Too small SMP PDU received\n"); + goto done; + } + + wlinfo("Received SMP code 0x%02x len %u\n", hdr->code, buf->len); + + bt_buf_pull(buf, sizeof(*hdr)); + + if (hdr->code >= NHANDLERS || !g_smp_handlers[hdr->code].func) + { + wlwarn("Unhandled SMP code 0x%02x\n", hdr->code); + err = BT_SMP_ERR_CMD_NOTSUPP; + } + else + { + if (!bt_atomic_testclrbit(&smp->allowed_cmds, hdr->code)) + { + wlwarn("Unexpected SMP code 0x%02x\n", hdr->code); + goto done; + } + + if (buf->len != g_smp_handlers[hdr->code].expect_len) + { + wlerr("ERROR: Invalid len %u for code 0x%02x\n", buf->len, hdr->code); + err = BT_SMP_ERR_INVALID_PARAMS; + } + else + { + err = g_smp_handlers[hdr->code].func(conn, buf); + } + } + + if (err) + { + send_err_rsp(conn, err); + + bt_atomic_set(&smp->allowed_cmds, 0); + + if (conn->role == BT_HCI_ROLE_MASTER) + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST); + } + else + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ); + } + } + +done: + bt_buf_put(buf); +} + +static void bt_smp_connected(FAR struct bt_conn_s *conn) +{ + int i; + + wlinfo("conn %p handle %u\n", conn, conn->handle); + + for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++) + { + struct bt_smp_s *smp = &g_smp_pool[i]; + + if (smp->conn) + { + continue; + } + + smp->conn = conn; + conn->smp = smp; + + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL); + + if (conn->role == BT_HCI_ROLE_MASTER) + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST); + } + else + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ); + } + + return; + } + + wlerr("ERROR: No available SMP context for conn %p\n", conn); +} + +static void bt_smp_disconnected(FAR struct bt_conn_s *conn) +{ + struct bt_smp_s *smp = conn->smp; + + if (!smp) + { + return; + } + + wlinfo("conn %p handle %u\n", conn, conn->handle); + + conn->smp = NULL; + memset(smp, 0, sizeof(*smp)); +} + +static void bt_smp_encrypt_change(FAR FAR struct bt_conn_s *conn) +{ + struct bt_smp_s *smp = conn->smp; + + wlinfo("conn %p handle %u encrypt 0x%02x\n", conn, conn->handle, + conn->encrypt); + + if (!smp || !conn->encrypt) + { + return; + } + + if (!smp->pending_encrypt) + { + return; + } + + smp->pending_encrypt = false; + + if (smp->remote_dist & BT_SMP_DIST_ENC_KEY) + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_ENCRYPT_INFO); + } + else if (smp->remote_dist & BT_SMP_DIST_ID_KEY) + { + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO); + } + + /* Slave distributes it's keys first */ + + if (conn->role == BT_HCI_ROLE_MASTER && smp->remote_dist) + { + return; + } + + bt_smp_distribute_keys(conn); +} + +#if defined(CONFIG_BLUETOOTH_SMP_SELFTEST) +/* spaw octets for LE encrypt */ + +static void swap_buf(FAR const uint8_t *src, FAR uint8_t *dst, uint16_t len) +{ + int i; + + for (i = 0; i < len; i++) + { + dst[len - 1 - i] = src[i]; + } +} + +static void swap_in_place(FAR uint8_t * buf, uint16_t len) +{ + int i; + int j; + + for (i = 0, j = len - 1; i < j; i++, j--) + { + uint8_t tmp = buf[i]; + + buf[i] = buf[j]; + buf[j] = tmp; + } +} + +/* 1 bit left shift */ + +static void array_shift(FAR const uint8_t *in, FAR uint8_t *out) +{ + uint8_t overflow = 0; + + for (int i = 15; i >= 0; i--) + { + out[i] = in[i] << 1; + /* previous byte */ + out[i] |= overflow; + overflow = in[i] & 0x80 ? 1 : 0; + } +} + +/* CMAC subkey generation algorithm */ + +static int cmac_subkey(FAR const uint8_t *key, FAR uint8_t *k1, + FAR uint8_t *k2) +{ + const uint8_t rb[16] = + { + [0 ... 14] = 0x00, + [15] = 0x87, + }; + uint8_t zero[16] = { 0 }; + uint8_t *tmp = zero; + uint8_t l[16]; + int err; + + /* L := AES-128(K, const_Zero) */ + err = le_encrypt(key, zero, tmp); + if (err) + { + return err; + } + + swap_buf(tmp, l, 16); + + wlinfo("l %s\n", h(l, 16)); + + /* if MSB(L) == 0 K1 = L << 1 */ + if (!(l[0] & 0x80)) + { + array_shift(l, k1); + /* else K1 = (L << 1) XOR rb */ + } + else + { + array_shift(l, k1); + xor_128((FAR struct uint128_s *)k1, (FAR struct uint128_s *)rb, (FAR struct uint128_s *)k1); + } + + /* if MSB(K1) == 0 K2 = K1 << 1 */ + if (!(k1[0] & 0x80)) + { + array_shift(k1, k2); + /* else K2 = (K1 << 1) XOR rb */ + } + else + { + array_shift(k1, k2); + xor_128((FAR struct uint128_s *)k2, (struct uint128_s *FAR )rb, (FAR struct uint128_s *)k2); + } + + return 0; +} + +/* padding(x) = x || 10^i where i is 128 - 8 * r - 1 */ + +static void add_pad(FAR const uint8_t *in, FAR unsigned char *out, int len) +{ + memset(out, 0, 16); + memcpy(out, in, len); + out[len] = 0x80; +} + +/* Cypher based Message Authentication Code (CMAC) with AES 128 bit + * + * Input : key ( 128-bit key ) + * : in ( message to be authenticated ) + * : len ( length of the message in octets ) + * Output : out ( message authentication code ) + */ + +static int bt_smp_aes_cmac(FAR const uint8_t *key, FAR const uint8_t *in, + size_t len, FAR uint8_t *out) +{ + uint8_t k1[16]; + uint8_t k2[16]; + uint8_t last_block[16]; + FAR uint8_t *pad_block = last_block; + uint8_t key_s[16]; + FAR uint8_t *x; + FAR uint8_t *y; + uint8_t flag; + uint8_t n; + int err; + + swap_buf(key, key_s, 16); + + /* (K1,K2) = Generate_Subkey(K) */ + err = cmac_subkey(key_s, k1, k2); + if (err) + { + return err; + } + + wlinfo("key %s subkeys k1 %s k2 %s\n", h(key, 16), h(k1, 16), h(k2, 16)); + + /* the number of blocks, n, is calculated, the block length is 16 bytes n = + * ceil(len/const_Bsize) + */ + + n = (len + 15) / 16; + + /* check input length, flag indicate completed blocks */ + + if (n == 0) + { + /* if length is 0, the number of blocks to be processed shall be 1,and + * the flag shall be marked as not-complete-block false. + */ + + n = 1; + flag = 0; + } + else + { + if ((len % 16) == 0) + { + /* complete blocks */ + flag = 1; + } + else + { + /* last block is not complete */ + flag = 0; + } + } + + wlinfo("len %u n %u flag %u\n", len, n, flag); + + /* if flag is true then M_last = M_n XOR K1 */ + if (flag) + { + xor_128((FAR struct uint128_s *)&in[16 * (n - 1)], + (FAR struct uint128_s *)k1, + (FAR struct uint128_s *)last_block); + + /* else M_last = padding(M_n) XOR K2 */ + } + else + { + add_pad(&in[16 * (n - 1)], pad_block, len % 16); + xor_128((FAR struct uint128_s *)pad_block, + (FAR struct uint128_s *)k2, + (FAR struct uint128_s *)last_block); + } + + /* Reuse k1 and k2 buffers */ + x = k1; + y = k2; + + /* Zeroing x */ + memset(x, 0, 16); + + /* Rhe basic CBC-MAC is applied to M_1,...,M_{n-1},M_last */ + + for (int i = 0; i < n - 1; i++) + { + /* Y = X XOR M_i */ + + xor_128((FAR struct uint128_s *)x, + (FAR struct uint128_s *)&in[i * 16], + (FAR struct uint128_s *) y); + + swap_in_place(y, 16); + + /* X = AES-128(K,Y) */ + + err = le_encrypt(key_s, y, x); + if (err) + { + return err; + } + + swap_in_place(x, 16); + } + + /* Y = M_last XOR X */ + + xor_128((FAR struct uint128_s *)x, + (FAR struct uint128_s *)last_block, + (FAR struct uint128_s *)y); + + swap_in_place(y, 16); + + /* T = AES-128(K,Y) */ + + err = le_encrypt(key_s, y, out); + + swap_in_place(out, 16); + + return err; +} + +static int aes_test(const char *prefix, const uint8_t *key, const uint8_t *m, + uint16_t len, const uint8_t * mac) +{ + uint8_t out[16]; + + wlinfo("%s: AES CMAC of message with len %u\n", prefix, len); + + bt_smp_aes_cmac(key, m, len, out); + if (!memcmp(out, mac, 16)) + { + wlinfo("%s: Success\n", prefix); + } + else + { + wlerr("ERROR: %s: Failed\n", prefix); + return -1; + } + + return 0; +} + +static int smp_aes_cmac_test(void) +{ + int err; + + err = aes_test("Test aes-cmac0", g_key, g_M, 0, g_mac1); + if (err) + { + return err; + } + + err = aes_test("Test aes-cmac16", g_key, g_M, 16, g_mac2); + if (err) + { + return err; + } + + err = aes_test("Test aes-cmac40", g_key, g_M, 40, g_mac3); + if (err) + { + return err; + } + + err = aes_test("Test aes-cmac64", g_key, g_M, 64, g_mac4); + if (err) + { + return err; + } + + return 0; +} + +static int smp_self_test(void) +{ + int err; + + err = smp_aes_cmac_test(); + if (err) + { + wlerr("ERROR: SMP AES-CMAC self tests failed\n"); + return err; + } + + return 0; +} +#else +static inline int smp_self_test(void) +{ + return 0; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int bt_smp_init(void) +{ + static struct bt_l2cap_chan_s chan = + { + .cid = BT_L2CAP_CID_SMP, + .recv = bt_smp_recv, + .connected = bt_smp_connected, + .disconnected = bt_smp_disconnected, + .encrypt_change = bt_smp_encrypt_change, + }; + + bt_l2cap_chan_register(&chan); + + return smp_self_test(); +} + +int bt_smp_send_security_req(FAR struct bt_conn_s *conn) +{ + struct bt_smp_security_request_s *req; + FAR struct bt_buf_s *req_buf; + + wlinfo("\n"); + + req_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_SECURITY_REQUEST, + sizeof(struct bt_smp_security_request_s)); + if (!req_buf) + { + return -ENOBUFS; + } + + req = bt_buf_add(req_buf, sizeof(struct bt_smp_security_request_s)); + req->auth_req = BT_SMP_AUTH_BONDING; + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, req_buf); + + return 0; +} + +int bt_smp_send_pairing_req(FAR struct bt_conn_s *conn) +{ + FAR struct bt_smp_s *smp = conn->smp; + FAR struct bt_smp_pairing_s *req; + FAR struct bt_buf_s *req_buf; + + wlinfo("\n"); + + if (smp_init(smp)) + { + return -ENOBUFS; + } + + req_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_REQ, sizeof(*req)); + if (!req_buf) + { + return -ENOBUFS; + } + + req = bt_buf_add(req_buf, sizeof(*req)); + + /* For JustWorks pairing simplify req parameters. TODO: needs to be reworked + * later on + */ + + req->auth_req = BT_SMP_AUTH_BONDING; + req->io_capability = BT_SMP_IO_NO_INPUT_OUTPUT; + req->oob_flag = BT_SMP_OOB_NOT_PRESENT; + req->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE; + req->init_key_dist = SEND_KEYS; + req->resp_key_dist = RECV_KEYS; + + smp->local_dist = SEND_KEYS; + smp->remote_dist = RECV_KEYS; + + memset(smp->tk, 0, sizeof(smp->tk)); + + /* Store req for later use */ + smp->preq[0] = BT_SMP_CMD_PAIRING_REQ; + + memcpy(smp->preq + 1, req, sizeof(*req)); + + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, req_buf); + + bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RSP); + return 0; +} + +bool bt_smp_irk_matches(FAR const uint8_t irk[16], FAR const bt_addr_t *addr) +{ + uint8_t hash[3]; + int err; + + wlinfo("IRK %s bdaddr %s", h(irk, 16), bt_addr_str(addr)); + + err = smp_ah(irk, addr->val + 3, hash); + if (err) + { + return false; + } + + return !memcmp(addr->val, hash, 3); +} diff --git a/wireless/bluetooth/bt_smp.h b/wireless/bluetooth/bt_smp.h new file mode 100644 index 00000000000..80ece113bdc --- /dev/null +++ b/wireless/bluetooth/bt_smp.h @@ -0,0 +1,179 @@ +/**************************************************************************** + * wireless/bluetooth/bt_smp.h + * Security Manager Protocol implementation. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __WIRELESS_BLUETOOTH_BT_SMP_H +#define __WIRELESS_BLUETOOTH_BT_SMP_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BT_SMP_ERR_PASSKEY_ENTRY_FAILED 0x01 +#define BT_SMP_ERR_OOB_NOT_AVAIL 0x02 +#define BT_SMP_ERR_AUTH_REQUIREMENTS 0x03 +#define BT_SMP_ERR_CONFIRM_FAILED 0x04 +#define BT_SMP_ERR_PAIRING_NOTSUPP 0x05 +#define BT_SMP_ERR_ENC_KEY_SIZE 0x06 +#define BT_SMP_ERR_CMD_NOTSUPP 0x07 +#define BT_SMP_ERR_UNSPECIFIED 0x08 +#define BT_SMP_ERR_REPEATED_ATTEMPTS 0x09 +#define BT_SMP_ERR_INVALID_PARAMS 0x0a +#define BT_SMP_ERR_DHKEY_CHECK_FAILED 0x0b +#define BT_SMP_ERR_NUMERIC_COMP_FAILED 0x0c +#define BT_SMP_ERR_BREDR_PAIRING_IN_PROGRESS 0x0d +#define BT_SMP_ERR_CROSS_TRANSP_NOT_ALLOWED 0x0e + +#define BT_SMP_IO_DISPLAY_ONLY 0x00 +#define BT_SMP_IO_DISPLAY_YESNO 0x01 +#define BT_SMP_IO_KEYBOARD_ONLY 0x02 +#define BT_SMP_IO_NO_INPUT_OUTPUT 0x03 +#define BT_SMP_IO_KEYBOARD_DISPLAY 0x04 + +#define BT_SMP_OOB_NOT_PRESENT 0x00 +#define BT_SMP_OOB_PRESENT 0x01 + +#define BT_SMP_MIN_ENC_KEY_SIZE 16 +#define BT_SMP_MAX_ENC_KEY_SIZE 16 + +#define BT_SMP_DIST_ENC_KEY 0x01 +#define BT_SMP_DIST_ID_KEY 0x02 +#define BT_SMP_DIST_SIGN 0x04 +#define BT_SMP_DIST_LINK_KEY 0x08 + +#define BT_SMP_DIST_MASK 0x0f + +#define BT_SMP_AUTH_NONE 0x00 +#define BT_SMP_AUTH_BONDING 0x01 +#define BT_SMP_AUTH_MITM 0x04 +#define BT_SMP_AUTH_SC 0x08 +#define BT_SMP_AUTH_KEYPRESS 0x10 + +#define BT_SMP_AUTH_MASK 0x1f + +#define BT_SMP_CMD_PAIRING_REQ 0x01 +#define BT_SMP_CMD_PAIRING_RSP 0x02 +#define BT_SMP_CMD_PAIRING_CONFIRM 0x03 +#define BT_SMP_CMD_PAIRING_RANDOM 0x04 +#define BT_SMP_CMD_PAIRING_FAIL 0x05 +#define BT_SMP_CMD_ENCRYPT_INFO 0x06 +#define BT_SMP_CMD_MASTER_IDENT 0x07 +#define BT_SMP_CMD_IDENT_INFO 0x08 +#define BT_SMP_CMD_IDENT_ADDR_INFO 0x09 +#define BT_SMP_CMD_SECURITY_REQUEST 0x0b + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +begin_packed_struct struct bt_smp_hdr_s +{ + uint8_t code; +} end_packed_struct; + +begin_packed_struct struct bt_smp_pairing_s +{ + uint8_t io_capability; + uint8_t oob_flag; + uint8_t auth_req; + uint8_t max_key_size; + uint8_t init_key_dist; + uint8_t resp_key_dist; +} end_packed_struct; + +begin_packed_struct struct bt_smp_pairing_confirm_s +{ + uint8_t val[16]; +} end_packed_struct; + +begin_packed_struct struct bt_smp_pairing_random_s +{ + uint8_t val[16]; +} end_packed_struct; + +begin_packed_struct struct bt_smp_pairing_fail_s +{ + uint8_t reason; +} end_packed_struct; + +begin_packed_struct struct bt_smp_encrypt_info_s +{ + uint8_t ltk[16]; +} end_packed_struct; + +begin_packed_struct struct bt_smp_master_ident_s +{ + uint16_t ediv; + uint64_t rand; +} end_packed_struct; + +begin_packed_struct struct bt_smp_ident_info_s +{ + uint8_t irk[16]; +} end_packed_struct; + +begin_packed_struct struct bt_smp_ident_addr_info_s +{ + bt_addr_le_t addr; +} end_packed_struct; + +begin_packed_struct struct bt_smp_security_request_s +{ + uint8_t auth_req; +} end_packed_struct; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +bool bt_smp_irk_matches(FAR const uint8_t irk[16], FAR const bt_addr_t *addr); +int bt_smp_send_pairing_req(FAR struct bt_conn_s *conn); +int bt_smp_send_security_req(FAR struct bt_conn_s *conn); +int bt_smp_init(void); + +#endif /* __WIRELESS_BLUETOOTH_BT_SMP_H */ diff --git a/wireless/bluetooth/bt_uuid.c b/wireless/bluetooth/bt_uuid.c new file mode 100644 index 00000000000..97a84061a99 --- /dev/null +++ b/wireless/bluetooth/bt_uuid.c @@ -0,0 +1,125 @@ +/**************************************************************************** + * wireless/bluetooth/bt_att.c + * Bluetooth UUID handling. + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package + * where the code was released with a compatible 3-clause BSD license: + * + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* TODO: Decide whether to continue using BLE format or switch to RFC 4122 */ + +static const struct bt_uuid_s g_uuid128_base = +{ + .type = BT_UUID_128, + .u.u128 = + { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + } +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void uuid_to_uuid128(FAR const struct bt_uuid_s *src, + FAR struct bt_uuid_s *dst) +{ + switch (src->type) + { + case BT_UUID_16: + *dst = g_uuid128_base; + memcpy(&dst->u.u128[2], &src->u.u16, sizeof(src->u.u16)); + return; + + case BT_UUID_128: + memcpy(dst, src, sizeof(*dst)); + return; + } +} + +static int uuid128_cmp(FAR const struct bt_uuid_s *u1, + FAR const struct bt_uuid_s *u2) +{ + struct bt_uuid_s uuid1; + struct bt_uuid_s uuid2; + + uuid_to_uuid128(u1, &uuid1); + uuid_to_uuid128(u2, &uuid2); + + return memcmp(uuid1.u.u128, uuid2.u.u128, sizeof(uuid1.u.u128)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int bt_uuid_cmp(FAR const struct bt_uuid_s *u1, + FAR const struct bt_uuid_s *u2) +{ + /* Convert to 128 bit if types don't match */ + + if (u1->type != u2->type) + { + return uuid128_cmp(u1, u2); + } + + switch (u1->type) + { + case BT_UUID_16: + return (int)u1->u.u16 - (int)u2->u.u16; + + case BT_UUID_128: + return memcmp(u1->u.u128, u2->u.u128, sizeof(u1->u.u128)); + } + + return -EINVAL; +}