Bluetooth: Add support for specifying connection parameters

Applications may want fine-grained control of connection parameters.
The two APIs to provide this through are bt_le_set_auto_conn() as well
as bt_conn_create_le().

Change-Id: If5cddbbf017b868d768d18d2a09daf4af8aa00d8
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2015-12-05 15:32:58 +02:00 committed by Anas Nashif
commit 1a32d5ed55
10 changed files with 81 additions and 48 deletions

View file

@ -157,12 +157,13 @@ int bt_le_scan_stop(void);
* will be re-established if connectable advertisement from peer is received. * will be re-established if connectable advertisement from peer is received.
* *
* @param addr Remote Bluetooth address. * @param addr Remote Bluetooth address.
* @param auto_conn boolean value. If true, auto connect is enabled, * @param param If non-NULL, auto connect is enabled with the given
* if false, auto connect is disabled. * parameters. If NULL, auto connect is disabled.
* *
* @return Zero on success or error code otherwise. * @return Zero on success or error code otherwise.
*/ */
int bt_le_set_auto_conn(bt_addr_le_t *addr, bool auto_conn); int bt_le_set_auto_conn(bt_addr_le_t *addr,
const struct bt_le_conn_param *param);
#endif /* CONFIG_BLUETOOTH_CENTRAL */ #endif /* CONFIG_BLUETOOTH_CENTRAL */

View file

@ -28,6 +28,17 @@
/** Opaque type representing a connection to a remote device */ /** Opaque type representing a connection to a remote device */
struct bt_conn; struct bt_conn;
/** Connection parameters for LE connections */
struct bt_le_conn_param {
uint16_t interval_min;
uint16_t interval_max;
};
#define BT_LE_CONN_PARAM_DEFAULT (&(struct bt_le_conn_param) { \
.interval_min = BT_GAP_INIT_CONN_INT_MIN, \
.interval_max = BT_GAP_INIT_CONN_INT_MAX, \
})
/** @brief Increment a connection's reference count. /** @brief Increment a connection's reference count.
* *
* Increment the reference count of a connection object. * Increment the reference count of a connection object.
@ -125,11 +136,13 @@ int bt_conn_disconnect(struct bt_conn *conn, uint8_t reason);
* Allows initiate new LE link to remote peer using its address. * Allows initiate new LE link to remote peer using its address.
* Returns a new reference that the the caller is responsible for managing. * Returns a new reference that the the caller is responsible for managing.
* *
* @param peer Remote address. * @param peer Remote address.
* @param param Initial connection parameters.
* *
* @return Valid connection object on success or NULL otherwise. * @return Valid connection object on success or NULL otherwise.
*/ */
struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer); struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer,
const struct bt_le_conn_param *param);
#endif #endif
/** Security level. */ /** Security level. */

View file

@ -527,6 +527,8 @@ struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer)
conn->required_sec_level = BT_SECURITY_LOW; conn->required_sec_level = BT_SECURITY_LOW;
#endif /* CONFIG_BLUETOOTH_SMP */ #endif /* CONFIG_BLUETOOTH_SMP */
conn->type = BT_CONN_TYPE_LE; conn->type = BT_CONN_TYPE_LE;
conn->le.interval_min = BT_GAP_INIT_CONN_INT_MIN;
conn->le.interval_max = BT_GAP_INIT_CONN_INT_MAX;
return conn; return conn;
} }
@ -824,7 +826,8 @@ int bt_conn_disconnect(struct bt_conn *conn, uint8_t reason)
} }
} }
struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer) struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer,
const struct bt_le_conn_param *param)
{ {
struct bt_conn *conn; struct bt_conn *conn;
@ -836,6 +839,9 @@ struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer)
if (conn) { if (conn) {
switch (conn->state) { switch (conn->state) {
case BT_CONN_CONNECT_SCAN: case BT_CONN_CONNECT_SCAN:
conn->le.interval_min = param->interval_min;
conn->le.interval_max = param->interval_max;
return conn;
case BT_CONN_CONNECT: case BT_CONN_CONNECT:
case BT_CONN_CONNECTED: case BT_CONN_CONNECTED:
return conn; return conn;
@ -850,6 +856,9 @@ struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer)
return NULL; return NULL;
} }
conn->le.interval_min = param->interval_min;
conn->le.interval_max = param->interval_max;
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
bt_le_scan_update(true); bt_le_scan_update(true);

View file

@ -37,7 +37,11 @@ struct bt_conn_le {
bt_addr_le_t init_addr; bt_addr_le_t init_addr;
bt_addr_le_t resp_addr; bt_addr_le_t resp_addr;
uint16_t conn_interval;
uint16_t interval;
uint16_t interval_min;
uint16_t interval_max;
uint8_t features[8]; uint8_t features[8];
}; };

View file

@ -392,7 +392,7 @@ static void hci_num_completed_packets(struct net_buf *buf)
} }
} }
static int hci_le_create_conn(const bt_addr_le_t *addr) static int hci_le_create_conn(const struct bt_conn *conn)
{ {
struct net_buf *buf; struct net_buf *buf;
struct bt_hci_cp_le_create_conn *cp; struct bt_hci_cp_le_create_conn *cp;
@ -409,9 +409,9 @@ static int hci_le_create_conn(const bt_addr_le_t *addr)
cp->scan_interval = sys_cpu_to_le16(BT_GAP_SCAN_FAST_INTERVAL); cp->scan_interval = sys_cpu_to_le16(BT_GAP_SCAN_FAST_INTERVAL);
cp->scan_window = cp->scan_interval; cp->scan_window = cp->scan_interval;
bt_addr_le_copy(&cp->peer_addr, addr); bt_addr_le_copy(&cp->peer_addr, &conn->le.resp_addr);
cp->conn_interval_max = sys_cpu_to_le16(BT_GAP_INIT_CONN_INT_MAX); cp->conn_interval_min = sys_cpu_to_le16(conn->le.interval_min);
cp->conn_interval_min = sys_cpu_to_le16(BT_GAP_INIT_CONN_INT_MIN); cp->conn_interval_max = sys_cpu_to_le16(conn->le.interval_max);
cp->supervision_timeout = sys_cpu_to_le16(0x07D0); cp->supervision_timeout = sys_cpu_to_le16(0x07D0);
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL); return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL);
@ -489,8 +489,8 @@ static int update_conn_params(struct bt_conn *conn)
BT_DBG("conn %p features 0x%x", conn, conn->le.features[0]); BT_DBG("conn %p features 0x%x", conn, conn->le.features[0]);
/* Check if there's a need to update conn params */ /* Check if there's a need to update conn params */
if (conn->le.conn_interval >= LE_CONN_MIN_INTERVAL && if (conn->le.interval >= conn->le.interval_min &&
conn->le.conn_interval <= LE_CONN_MAX_INTERVAL) { conn->le.interval <= conn->le.interval_max) {
return -EALREADY; return -EALREADY;
} }
@ -501,9 +501,10 @@ static int update_conn_params(struct bt_conn *conn)
if ((conn->le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC) && if ((conn->le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC) &&
(bt_dev.le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC)) { (bt_dev.le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC)) {
return bt_conn_le_conn_update(conn, LE_CONN_MIN_INTERVAL, return bt_conn_le_conn_update(conn, conn->le.interval_min,
LE_CONN_MAX_INTERVAL, conn->le.interval_max,
LE_CONN_LATENCY, LE_CONN_TIMEOUT); LE_CONN_LATENCY,
LE_CONN_TIMEOUT);
} }
return -EBUSY; return -EBUSY;
@ -555,7 +556,7 @@ static void le_conn_complete(struct net_buf *buf)
conn->handle = handle; conn->handle = handle;
bt_addr_le_copy(&conn->le.dst, id_addr); bt_addr_le_copy(&conn->le.dst, id_addr);
conn->le.conn_interval = sys_le16_to_cpu(evt->interval); conn->le.interval = sys_le16_to_cpu(evt->interval);
conn->role = evt->role; conn->role = evt->role;
src.type = BT_ADDR_LE_PUBLIC; src.type = BT_ADDR_LE_PUBLIC;
@ -699,7 +700,7 @@ static void le_conn_update_complete(struct net_buf *buf)
} }
if (!evt->status) { if (!evt->status) {
conn->le.conn_interval = interval; conn->le.interval = interval;
} }
/* TODO Notify about connection */ /* TODO Notify about connection */
@ -731,7 +732,9 @@ static void check_pending_conn(const bt_addr_le_t *id_addr,
goto done; goto done;
} }
if (hci_le_create_conn(addr)) { bt_addr_le_copy(&conn->le.resp_addr, addr);
if (hci_le_create_conn(conn)) {
goto done; goto done;
} }
@ -1214,7 +1217,8 @@ int bt_le_scan_update(bool fast_scan)
} }
#if defined(CONFIG_BLUETOOTH_CENTRAL) #if defined(CONFIG_BLUETOOTH_CENTRAL)
int bt_le_set_auto_conn(bt_addr_le_t *addr, bool auto_conn) int bt_le_set_auto_conn(bt_addr_le_t *addr,
const struct bt_le_conn_param *param)
{ {
struct bt_conn *conn; struct bt_conn *conn;
@ -1226,7 +1230,10 @@ int bt_le_set_auto_conn(bt_addr_le_t *addr, bool auto_conn)
} }
} }
if (auto_conn) { if (param) {
conn->le.interval_min = param->interval_min;
conn->le.interval_max = param->interval_max;
if (!atomic_test_and_set_bit(conn->flags, if (!atomic_test_and_set_bit(conn->flags,
BT_CONN_AUTO_CONNECT)) { BT_CONN_AUTO_CONNECT)) {
bt_conn_ref(conn); bt_conn_ref(conn);
@ -1243,7 +1250,7 @@ int bt_le_set_auto_conn(bt_addr_le_t *addr, bool auto_conn)
if (conn->state == BT_CONN_DISCONNECTED && if (conn->state == BT_CONN_DISCONNECTED &&
atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
if (auto_conn) { if (param) {
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
} }
bt_le_scan_update(false); bt_le_scan_update(false);

View file

@ -33,8 +33,6 @@
#define lmp_le_capable(dev) ((dev).features[4] & BT_LMP_LE) #define lmp_le_capable(dev) ((dev).features[4] & BT_LMP_LE)
/* LL connection parameters */ /* LL connection parameters */
#define LE_CONN_MIN_INTERVAL 0x0028
#define LE_CONN_MAX_INTERVAL 0x0038
#define LE_CONN_LATENCY 0x0000 #define LE_CONN_LATENCY 0x0000
#define LE_CONN_TIMEOUT 0x002a #define LE_CONN_TIMEOUT 0x002a

View file

@ -974,8 +974,8 @@ int bt_l2cap_update_conn_param(struct bt_conn *conn)
hdr->len = sys_cpu_to_le16(sizeof(*req)); hdr->len = sys_cpu_to_le16(sizeof(*req));
req = net_buf_add(buf, sizeof(*req)); req = net_buf_add(buf, sizeof(*req));
req->min_interval = sys_cpu_to_le16(LE_CONN_MIN_INTERVAL); req->min_interval = sys_cpu_to_le16(conn->le.interval_min);
req->max_interval = sys_cpu_to_le16(LE_CONN_MAX_INTERVAL); req->max_interval = sys_cpu_to_le16(conn->le.interval_max);
req->latency = sys_cpu_to_le16(LE_CONN_LATENCY); req->latency = sys_cpu_to_le16(LE_CONN_LATENCY);
req->timeout = sys_cpu_to_le16(LE_CONN_TIMEOUT); req->timeout = sys_cpu_to_le16(LE_CONN_TIMEOUT);

View file

@ -130,7 +130,6 @@ static void connected(struct bt_conn *conn)
static bool eir_found(const struct bt_eir *eir, void *user_data) static bool eir_found(const struct bt_eir *eir, void *user_data)
{ {
bt_addr_le_t *addr = user_data; bt_addr_le_t *addr = user_data;
uint16_t u16;
int i; int i;
printk("[AD]: %u len %u\n", eir->type, eir->len); printk("[AD]: %u len %u\n", eir->type, eir->len);
@ -138,24 +137,29 @@ static bool eir_found(const struct bt_eir *eir, void *user_data)
switch (eir->type) { switch (eir->type) {
case BT_EIR_UUID16_SOME: case BT_EIR_UUID16_SOME:
case BT_EIR_UUID16_ALL: case BT_EIR_UUID16_ALL:
if ((eir->len - sizeof(eir->type)) % sizeof(u16) != 0) { if ((eir->len - sizeof(eir->type)) % sizeof(uint16_t) != 0) {
printk("AD malformed\n"); printk("AD malformed\n");
return true; return true;
} }
for (i = 0; i < eir->len; i += sizeof(u16)) { for (i = 0; i < eir->len; i += sizeof(uint16_t)) {
uint16_t u16;
int err;
memcpy(&u16, &eir->data[i], sizeof(u16)); memcpy(&u16, &eir->data[i], sizeof(u16));
if (sys_le16_to_cpu(u16) == BT_UUID_HRS) { if (sys_le16_to_cpu(u16) != BT_UUID_HRS) {
int err = bt_le_scan_stop(); continue;
if (err) {
printk("Stopping scanning failed"
" (err %d)\n", err);
}
default_conn = bt_conn_create_le(addr);
return false;
} }
err = bt_le_scan_stop();
if (err) {
printk("Stop LE scan failed (err %d)\n", err);
continue;
}
default_conn = bt_conn_create_le(addr,
BT_LE_CONN_PARAM_DEFAULT);
return false;
} }
} }

View file

@ -236,7 +236,7 @@ static void cmd_connect_le(int argc, char *argv[])
return; return;
} }
conn = bt_conn_create_le(&addr); conn = bt_conn_create_le(&addr, BT_LE_CONN_PARAM_DEFAULT);
if (!conn) { if (!conn) {
printk("Connection failed\n"); printk("Connection failed\n");
@ -294,7 +294,6 @@ static void cmd_disconnect(int argc, char *argv[])
static void cmd_auto_conn(int argc, char *argv[]) static void cmd_auto_conn(int argc, char *argv[])
{ {
bt_addr_le_t addr; bt_addr_le_t addr;
bool enable;
int err; int err;
if (argc < 2) { if (argc < 2) {
@ -314,17 +313,14 @@ static void cmd_auto_conn(int argc, char *argv[])
} }
if (argc < 4) { if (argc < 4) {
enable = true; bt_le_set_auto_conn(&addr, BT_LE_CONN_PARAM_DEFAULT);
} else if (!strcmp(argv[3], "on")) { } else if (!strcmp(argv[3], "on")) {
enable = true; bt_le_set_auto_conn(&addr, BT_LE_CONN_PARAM_DEFAULT);
} else if (!strcmp(argv[3], "off")) { } else if (!strcmp(argv[3], "off")) {
enable = false; bt_le_set_auto_conn(&addr, NULL);
} else { } else {
printk("Specify \"on\" or \"off\"\n"); printk("Specify \"on\" or \"off\"\n");
return;
} }
bt_le_set_auto_conn(&addr, enable);
} }
static void cmd_select(int argc, char *argv[]) static void cmd_select(int argc, char *argv[])

View file

@ -326,7 +326,8 @@ static void connect(const uint8_t *data, uint16_t len)
struct bt_conn *conn; struct bt_conn *conn;
uint8_t status; uint8_t status;
conn = bt_conn_create_le((bt_addr_le_t *) data); conn = bt_conn_create_le((bt_addr_le_t *) data,
BT_LE_CONN_PARAM_DEFAULT);
if (!conn) { if (!conn) {
status = BTP_STATUS_FAILED; status = BTP_STATUS_FAILED;
goto rsp; goto rsp;