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:
Johan Hedberg 2015-04-14 15:44:48 +03:00 committed by Anas Nashif
commit d210d306d7
2 changed files with 256 additions and 18 deletions

View file

@ -101,6 +101,11 @@ size_t bt_buf_headroom(struct bt_buf *buf);
/* Return pointer to the end of the data in the buffer */ /* Return pointer to the end of the data in the buffer */
#define bt_buf_tail(buf) ((buf)->data + (buf)->len) #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. */ /* Initialize Bluetooth. Must be the called before anything else. */
int bt_init(void); int bt_init(void);

View file

@ -39,6 +39,10 @@
#include <bluetooth/hci.h> #include <bluetooth/hci.h>
#include <bluetooth/bluetooth.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 */ /* Stacks for the fibers */
#define RX_STACK_SIZE 1024 #define RX_STACK_SIZE 1024
#define CMD_STACK_SIZE 256 #define CMD_STACK_SIZE 256
@ -52,6 +56,24 @@ static struct nano_fifo free_bufs;
/* State tracking for the local Bluetooth controller */ /* State tracking for the local Bluetooth controller */
static struct bt_dev { 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 */ /* Number of commands controller can accept */
uint8_t ncmd; uint8_t ncmd;
struct nano_sem ncmd_sem; struct nano_sem ncmd_sem;
@ -171,9 +193,49 @@ static int bt_hci_cmd_send(uint16_t opcode, struct bt_buf *buf)
return 0; 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) 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 */ /* HCI event processing */
@ -188,6 +250,80 @@ static void hci_reset_complete(struct bt_buf *buf)
return; 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) static void hci_cmd_done(uint16_t opcode)
{ {
struct bt_buf *sent = dev.sent_cmd; struct bt_buf *sent = dev.sent_cmd;
@ -199,6 +335,10 @@ static void hci_cmd_done(uint16_t opcode)
dev.sent_cmd = NULL; 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); bt_buf_put(sent);
} }
@ -215,6 +355,24 @@ static void hci_cmd_complete(struct bt_buf *buf)
case BT_HCI_OP_RESET: case BT_HCI_OP_RESET:
hci_reset_complete(buf); hci_reset_complete(buf);
break; 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: default:
BT_ERR("Unknown opcode %x\n", opcode); BT_ERR("Unknown opcode %x\n", opcode);
break; break;
@ -271,23 +429,10 @@ static void hci_event(struct bt_buf *buf)
default: default:
BT_ERR("Unknown event %u\n", hdr->evt); BT_ERR("Unknown event %u\n", hdr->evt);
break; 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) static void hci_cmd_fiber(void)
@ -322,19 +467,107 @@ static void hci_rx_fiber(void)
while (1) { while (1) {
buf = nano_fifo_get_wait(&dev.rx_queue); buf = nano_fifo_get_wait(&dev.rx_queue);
hci_receive_packet(buf); BT_DBG("buf %p type %u len %u\n", buf, buf->type, buf->len);
bt_buf_put(buf);
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) static int hci_init(void)
{ {
struct bt_hci_cp_set_event_mask *ev;
struct bt_buf *buf;
/* Send HCI_RESET */ /* Send HCI_RESET */
bt_hci_cmd_send(BT_HCI_OP_RESET, NULL); 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; return 0;
} }
int bt_hci_reset(void)
{
return hci_init();
}
/* Interface to HCI driver layer */ /* Interface to HCI driver layer */
void bt_recv(struct bt_buf *buf) void bt_recv(struct bt_buf *buf)