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.
*
* @param addr Remote Bluetooth address.
* @param auto_conn boolean value. If true, auto connect is enabled,
* if false, auto connect is disabled.
* @param param If non-NULL, auto connect is enabled with the given
* parameters. If NULL, auto connect is disabled.
*
* @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 */

View file

@ -28,6 +28,17 @@
/** Opaque type representing a connection to a remote device */
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.
*
* Increment the reference count of a connection object.
@ -126,10 +137,12 @@ int bt_conn_disconnect(struct bt_conn *conn, uint8_t reason);
* Returns a new reference that the the caller is responsible for managing.
*
* @param peer Remote address.
* @param param Initial connection parameters.
*
* @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
/** 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;
#endif /* CONFIG_BLUETOOTH_SMP */
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;
}
@ -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;
@ -836,6 +839,9 @@ struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer)
if (conn) {
switch (conn->state) {
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_CONNECTED:
return conn;
@ -850,6 +856,9 @@ struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer)
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_le_scan_update(true);

View file

@ -37,7 +37,11 @@ struct bt_conn_le {
bt_addr_le_t init_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];
};

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 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_window = cp->scan_interval;
bt_addr_le_copy(&cp->peer_addr, addr);
cp->conn_interval_max = sys_cpu_to_le16(BT_GAP_INIT_CONN_INT_MAX);
cp->conn_interval_min = sys_cpu_to_le16(BT_GAP_INIT_CONN_INT_MIN);
bt_addr_le_copy(&cp->peer_addr, &conn->le.resp_addr);
cp->conn_interval_min = sys_cpu_to_le16(conn->le.interval_min);
cp->conn_interval_max = sys_cpu_to_le16(conn->le.interval_max);
cp->supervision_timeout = sys_cpu_to_le16(0x07D0);
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]);
/* Check if there's a need to update conn params */
if (conn->le.conn_interval >= LE_CONN_MIN_INTERVAL &&
conn->le.conn_interval <= LE_CONN_MAX_INTERVAL) {
if (conn->le.interval >= conn->le.interval_min &&
conn->le.interval <= conn->le.interval_max) {
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) &&
(bt_dev.le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC)) {
return bt_conn_le_conn_update(conn, LE_CONN_MIN_INTERVAL,
LE_CONN_MAX_INTERVAL,
LE_CONN_LATENCY, LE_CONN_TIMEOUT);
return bt_conn_le_conn_update(conn, conn->le.interval_min,
conn->le.interval_max,
LE_CONN_LATENCY,
LE_CONN_TIMEOUT);
}
return -EBUSY;
@ -555,7 +556,7 @@ static void le_conn_complete(struct net_buf *buf)
conn->handle = handle;
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;
src.type = BT_ADDR_LE_PUBLIC;
@ -699,7 +700,7 @@ static void le_conn_update_complete(struct net_buf *buf)
}
if (!evt->status) {
conn->le.conn_interval = interval;
conn->le.interval = interval;
}
/* TODO Notify about connection */
@ -731,7 +732,9 @@ static void check_pending_conn(const bt_addr_le_t *id_addr,
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;
}
@ -1214,7 +1217,8 @@ int bt_le_scan_update(bool fast_scan)
}
#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;
@ -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,
BT_CONN_AUTO_CONNECT)) {
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 &&
atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
if (auto_conn) {
if (param) {
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
}
bt_le_scan_update(false);

View file

@ -33,8 +33,6 @@
#define lmp_le_capable(dev) ((dev).features[4] & BT_LMP_LE)
/* LL connection parameters */
#define LE_CONN_MIN_INTERVAL 0x0028
#define LE_CONN_MAX_INTERVAL 0x0038
#define LE_CONN_LATENCY 0x0000
#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));
req = net_buf_add(buf, sizeof(*req));
req->min_interval = sys_cpu_to_le16(LE_CONN_MIN_INTERVAL);
req->max_interval = sys_cpu_to_le16(LE_CONN_MAX_INTERVAL);
req->min_interval = sys_cpu_to_le16(conn->le.interval_min);
req->max_interval = sys_cpu_to_le16(conn->le.interval_max);
req->latency = sys_cpu_to_le16(LE_CONN_LATENCY);
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)
{
bt_addr_le_t *addr = user_data;
uint16_t u16;
int i;
printk("[AD]: %u len %u\n", eir->type, eir->len);
@ -138,26 +137,31 @@ static bool eir_found(const struct bt_eir *eir, void *user_data)
switch (eir->type) {
case BT_EIR_UUID16_SOME:
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");
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));
if (sys_le16_to_cpu(u16) == BT_UUID_HRS) {
int err = bt_le_scan_stop();
if (sys_le16_to_cpu(u16) != BT_UUID_HRS) {
continue;
}
err = bt_le_scan_stop();
if (err) {
printk("Stopping scanning failed"
" (err %d)\n", err);
printk("Stop LE scan failed (err %d)\n", err);
continue;
}
default_conn = bt_conn_create_le(addr);
default_conn = bt_conn_create_le(addr,
BT_LE_CONN_PARAM_DEFAULT);
return false;
}
}
}
return true;
}

View file

@ -236,7 +236,7 @@ static void cmd_connect_le(int argc, char *argv[])
return;
}
conn = bt_conn_create_le(&addr);
conn = bt_conn_create_le(&addr, BT_LE_CONN_PARAM_DEFAULT);
if (!conn) {
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[])
{
bt_addr_le_t addr;
bool enable;
int err;
if (argc < 2) {
@ -314,17 +313,14 @@ static void cmd_auto_conn(int argc, char *argv[])
}
if (argc < 4) {
enable = true;
bt_le_set_auto_conn(&addr, BT_LE_CONN_PARAM_DEFAULT);
} 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")) {
enable = false;
bt_le_set_auto_conn(&addr, NULL);
} else {
printk("Specify \"on\" or \"off\"\n");
return;
}
bt_le_set_auto_conn(&addr, enable);
}
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;
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) {
status = BTP_STATUS_FAILED;
goto rsp;