Bluetooth: Add full HCI initialization routine
This patch adds a full HCI initialization routine to retrieve all relevant information from the controller. We also need to introduce a new blocking bt_hci_cmd_send_sync() API since some commands are conditional to the results of others. The API is implemented with the help of a semaphore that's part of the command's bt_buf struct. We wait on the semaphore and get it back once the command has completed (with the help of the hci_cmd_done function). The patch also adds variables for storing various controller specific parameters which will be needed later during the operation of the stack. These variables are part of 'struct bt_dev' and get updated through the respective command complete handlers. A new bt_hci_reset() API is added which allows an application to reset the controller state at any time by re-running the HCI init procedure. Change-Id: I5b1a38e910d79ad5fe806467bc51388eedc9c8f9 Co-authored-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com> Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
45554f5b72
commit
d210d306d7
2 changed files with 256 additions and 18 deletions
|
@ -101,6 +101,11 @@ size_t bt_buf_headroom(struct bt_buf *buf);
|
|||
/* Return pointer to the end of the data in the buffer */
|
||||
#define bt_buf_tail(buf) ((buf)->data + (buf)->len)
|
||||
|
||||
/* HCI control APIs */
|
||||
|
||||
/* Reset the state of the controller (i.e. perform full HCI init */
|
||||
int bt_hci_reset(void);
|
||||
|
||||
/* Initialize Bluetooth. Must be the called before anything else. */
|
||||
int bt_init(void);
|
||||
|
||||
|
|
|
@ -39,6 +39,10 @@
|
|||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
||||
/* 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)
|
||||
|
||||
/* Stacks for the fibers */
|
||||
#define RX_STACK_SIZE 1024
|
||||
#define CMD_STACK_SIZE 256
|
||||
|
@ -52,6 +56,24 @@ static struct nano_fifo free_bufs;
|
|||
|
||||
/* State tracking for the local Bluetooth controller */
|
||||
static struct bt_dev {
|
||||
/* Local Bluetooth Device Address */
|
||||
uint8_t bdaddr[6];
|
||||
|
||||
/* 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];
|
||||
|
||||
/* Controller buffer information */
|
||||
uint16_t le_mtu;
|
||||
uint8_t le_pkts;
|
||||
|
||||
/* Number of commands controller can accept */
|
||||
uint8_t ncmd;
|
||||
struct nano_sem ncmd_sem;
|
||||
|
@ -171,9 +193,49 @@ static int bt_hci_cmd_send(uint16_t opcode, struct bt_buf *buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bt_hci_cmd_send_sync(uint16_t opcode, struct bt_buf *buf)
|
||||
{
|
||||
struct nano_sem sync_sem;
|
||||
|
||||
if (!buf) {
|
||||
buf = bt_hci_cmd_create(opcode, 0);
|
||||
if (!buf)
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
BT_DBG("opcode %x len %u\n", opcode, buf->len);
|
||||
|
||||
nano_sem_init(&sync_sem);
|
||||
buf->sync = &sync_sem;
|
||||
|
||||
nano_fifo_put(&dev.cmd_queue, buf);
|
||||
|
||||
nano_sem_take_wait(&sync_sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hci_acl(struct bt_buf *buf)
|
||||
{
|
||||
BT_DBG("\n");
|
||||
struct bt_hci_acl_hdr *hdr = (void *)buf->data;
|
||||
uint16_t handle, len = sys_le16_to_cpu(hdr->len);
|
||||
uint8_t flags;
|
||||
|
||||
handle = sys_le16_to_cpu(hdr->handle);
|
||||
flags = (handle >> 12);
|
||||
handle = bt_acl_handle(handle);
|
||||
|
||||
bt_buf_pull(buf, sizeof(*hdr));
|
||||
|
||||
BT_DBG("handle %u len %u flags %u\n", handle, len, flags);
|
||||
|
||||
if (buf->len != len) {
|
||||
BT_ERR("ACL data length mismatch (%u != %u)\n", buf->len, len);
|
||||
bt_buf_put(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_buf_put(buf);
|
||||
}
|
||||
|
||||
/* HCI event processing */
|
||||
|
@ -188,6 +250,80 @@ static void hci_reset_complete(struct bt_buf *buf)
|
|||
return;
|
||||
}
|
||||
|
||||
static void hci_read_local_ver_complete(struct bt_buf *buf)
|
||||
{
|
||||
struct bt_hci_rp_read_local_version_info *rp = (void *)buf->data;
|
||||
|
||||
BT_DBG("status %u\n", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
dev.hci_version = rp->hci_version;
|
||||
dev.hci_revision = sys_le16_to_cpu(rp->hci_revision);
|
||||
dev.manufacturer = sys_le16_to_cpu(rp->manufacturer);
|
||||
}
|
||||
|
||||
static void hci_read_features_complete(struct bt_buf *buf)
|
||||
{
|
||||
struct bt_hci_rp_read_local_features *rp = (void *)buf->data;
|
||||
|
||||
BT_DBG("status %u\n", rp->status);
|
||||
|
||||
memcpy(dev.features, rp->features, sizeof(dev.features));
|
||||
}
|
||||
|
||||
static void hci_read_buffer_size_complete(struct bt_buf *buf)
|
||||
{
|
||||
struct bt_hci_rp_read_buffer_size *rp = (void *)buf->data;
|
||||
|
||||
BT_DBG("status %u\n", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
/* If LE-side has buffers we can ignore the BR/EDR values */
|
||||
if (dev.le_mtu)
|
||||
return;
|
||||
|
||||
dev.le_mtu = sys_le16_to_cpu(rp->acl_max_len);
|
||||
dev.le_pkts = sys_le16_to_cpu(rp->acl_max_num);
|
||||
}
|
||||
|
||||
static void hci_read_bdaddr_complete(struct bt_buf *buf)
|
||||
{
|
||||
struct bt_hci_rp_read_bd_addr *rp = (void *)buf->data;
|
||||
|
||||
BT_DBG("status %u\n", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
memcpy(dev.bdaddr, rp->bdaddr, sizeof(dev.bdaddr));
|
||||
}
|
||||
|
||||
static void hci_le_read_buffer_size_complete(struct bt_buf *buf)
|
||||
{
|
||||
struct bt_hci_rp_le_read_buffer_size *rp = (void *)buf->data;
|
||||
|
||||
BT_DBG("status %u\n", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
dev.le_mtu = sys_le16_to_cpu(rp->le_max_len);
|
||||
dev.le_pkts = rp->le_max_num;
|
||||
}
|
||||
|
||||
static void hci_le_read_features_complete(struct bt_buf *buf)
|
||||
{
|
||||
struct bt_hci_rp_le_read_local_features *rp = (void *)buf->data;
|
||||
|
||||
BT_DBG("status %u\n", rp->status);
|
||||
|
||||
memcpy(dev.le_features, rp->features, sizeof(dev.le_features));
|
||||
}
|
||||
|
||||
static void hci_cmd_done(uint16_t opcode)
|
||||
{
|
||||
struct bt_buf *sent = dev.sent_cmd;
|
||||
|
@ -199,6 +335,10 @@ static void hci_cmd_done(uint16_t opcode)
|
|||
|
||||
dev.sent_cmd = NULL;
|
||||
|
||||
/* If the command was synchronous wake up bt_hci_cmd_send_sync() */
|
||||
if (sent->sync)
|
||||
nano_fiber_sem_give(sent->sync);
|
||||
|
||||
bt_buf_put(sent);
|
||||
}
|
||||
|
||||
|
@ -215,6 +355,24 @@ static void hci_cmd_complete(struct bt_buf *buf)
|
|||
case BT_HCI_OP_RESET:
|
||||
hci_reset_complete(buf);
|
||||
break;
|
||||
case BT_HCI_OP_READ_LOCAL_VERSION_INFO:
|
||||
hci_read_local_ver_complete(buf);
|
||||
break;
|
||||
case BT_HCI_OP_READ_LOCAL_FEATURES:
|
||||
hci_read_features_complete(buf);
|
||||
break;
|
||||
case BT_HCI_OP_READ_BUFFER_SIZE:
|
||||
hci_read_buffer_size_complete(buf);
|
||||
break;
|
||||
case BT_HCI_OP_READ_BD_ADDR:
|
||||
hci_read_bdaddr_complete(buf);
|
||||
break;
|
||||
case BT_HCI_OP_LE_READ_BUFFER_SIZE:
|
||||
hci_le_read_buffer_size_complete(buf);
|
||||
break;
|
||||
case BT_HCI_OP_LE_READ_LOCAL_FEATURES:
|
||||
hci_le_read_features_complete(buf);
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Unknown opcode %x\n", opcode);
|
||||
break;
|
||||
|
@ -271,23 +429,10 @@ static void hci_event(struct bt_buf *buf)
|
|||
default:
|
||||
BT_ERR("Unknown event %u\n", hdr->evt);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void hci_receive_packet(struct bt_buf *buf)
|
||||
{
|
||||
BT_DBG("buf %p type %u\n", buf, buf->type);
|
||||
|
||||
switch (buf->type) {
|
||||
case BT_ACL:
|
||||
hci_acl(buf);
|
||||
break;
|
||||
case BT_EVT:
|
||||
hci_event(buf);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
bt_buf_put(buf);
|
||||
}
|
||||
|
||||
static void hci_cmd_fiber(void)
|
||||
|
@ -322,19 +467,107 @@ static void hci_rx_fiber(void)
|
|||
while (1) {
|
||||
buf = nano_fifo_get_wait(&dev.rx_queue);
|
||||
|
||||
hci_receive_packet(buf);
|
||||
bt_buf_put(buf);
|
||||
BT_DBG("buf %p type %u len %u\n", buf, buf->type, buf->len);
|
||||
|
||||
switch (buf->type) {
|
||||
case BT_ACL:
|
||||
hci_acl(buf);
|
||||
break;
|
||||
case BT_EVT:
|
||||
hci_event(buf);
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Unknown buf type %u\n", buf->type);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int hci_init(void)
|
||||
{
|
||||
struct bt_hci_cp_set_event_mask *ev;
|
||||
struct bt_buf *buf;
|
||||
|
||||
/* Send HCI_RESET */
|
||||
bt_hci_cmd_send(BT_HCI_OP_RESET, NULL);
|
||||
|
||||
/* Read Local Supported Features */
|
||||
bt_hci_cmd_send(BT_HCI_OP_READ_LOCAL_FEATURES, NULL);
|
||||
|
||||
/* Read Local Version Information */
|
||||
bt_hci_cmd_send(BT_HCI_OP_READ_LOCAL_VERSION_INFO, NULL);
|
||||
|
||||
/* Read Bluetooth Address */
|
||||
bt_hci_cmd_send_sync(BT_HCI_OP_READ_BD_ADDR, NULL);
|
||||
|
||||
/* For now we only support LE capable controllers */
|
||||
if (!lmp_le_capable(dev)) {
|
||||
BT_ERR("Non-LE capable controller detected!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Read Low Energy Supported Features */
|
||||
bt_hci_cmd_send(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL);
|
||||
|
||||
/* Read LE Buffer Size */
|
||||
bt_hci_cmd_send(BT_HCI_OP_LE_READ_BUFFER_SIZE, NULL);
|
||||
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_SET_EVENT_MASK, 8);
|
||||
if (!buf)
|
||||
return -ENOBUFS;
|
||||
|
||||
ev = (void *)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 (dev.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);
|
||||
|
||||
if (lmp_bredr_capable(dev)) {
|
||||
struct bt_hci_cp_write_le_host_supp *cp;
|
||||
|
||||
/* Use BR/EDR buffer size if LE reports zero buffers */
|
||||
if (!dev.le_mtu)
|
||||
bt_hci_cmd_send(BT_HCI_OP_READ_BUFFER_SIZE, NULL);
|
||||
|
||||
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP,
|
||||
sizeof(*cp));
|
||||
if (!buf)
|
||||
return -ENOBUFS;
|
||||
|
||||
cp = (void *)bt_buf_add(buf, sizeof*cp);
|
||||
|
||||
/* Excplicitly enable LE for dual-mode controllers */
|
||||
cp->le = 0x01;
|
||||
cp->simul = 0x00;
|
||||
bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf);
|
||||
}
|
||||
|
||||
BT_DBG("HCI ver %u rev %u, manufacturer %u\n", dev.hci_version,
|
||||
dev.hci_revision, dev.manufacturer);
|
||||
BT_DBG("ACL buffers: pkts %u mtu %u\n", dev.le_pkts, dev.le_mtu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_hci_reset(void)
|
||||
{
|
||||
return hci_init();
|
||||
}
|
||||
|
||||
/* Interface to HCI driver layer */
|
||||
|
||||
void bt_recv(struct bt_buf *buf)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue