From 4bd7eacb94e2ada8b4595c3c6b7e772fe3e0ddf9 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 11:58:22 +0530 Subject: [PATCH] Bluetooth: Add LE connection parameter update procedure Added new MGMT command to update LE connection parameters Change-Id: I6ae16513437cd42d40e75958aa8415baa1cbedbb Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar Signed-off-by: Wootak Jung --- include/net/bluetooth/hci_core.h | 5 ++ include/net/bluetooth/mgmt_tizen.h | 28 +++++++++++ net/bluetooth/hci_event.c | 14 ++++++ net/bluetooth/mgmt.c | 98 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8121d9c..5f560e1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2190,6 +2190,11 @@ void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status); int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name, u8 name_len); void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering); +int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, + u8 dst_type, u16 conn_interval, u16 conn_latency, + u16 supervision_timeout); +int mgmt_le_conn_update_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status); #endif int hci_abort_conn(struct hci_conn *conn, u8 reason); diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index fab5beb..c912a69 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -123,6 +123,18 @@ struct mgmt_cp_stop_le_discovery { #define MGMT_DISABLE_LE_AUTO_CONNECT_SIZE 0 /* LE auto connection */ +/* For Add LE connection parameter update procedure */ +#define MGMT_LE_CONN_UPDATE_SIZE 14 +#define MGMT_OP_LE_CONN_UPDATE (TIZEN_OP_CODE_BASE + 0x0d) +struct mgmt_cp_le_conn_update { + __le16 conn_interval_min; + __le16 conn_interval_max; + __le16 conn_latency; + __le16 supervision_timeout; + bdaddr_t bdaddr; +} __packed; +/* Add LE connection parameter update procedure */ + /* EVENTS */ /* For device name update changes */ @@ -156,4 +168,20 @@ struct mgmt_cc_rp_get_raw_rssi { #define MGMT_EV_RSSI_DISABLED (TIZEN_EV_BASE + 0x07) /* Handling of RSSI Events */ +/* For Add LE connection update Events */ +#define MGMT_EV_CONN_UPDATED (TIZEN_EV_BASE + 0x08) +struct mgmt_ev_conn_updated { + struct mgmt_addr_info addr; + __le16 conn_interval; + __le16 conn_latency; + __le16 supervision_timeout; +} __packed; + +#define MGMT_EV_CONN_UPDATE_FAILED (TIZEN_EV_BASE + 0x09) +struct mgmt_ev_conn_update_failed { + struct mgmt_addr_info addr; + __u8 status; +} __packed; +/* Add LE connection update Events */ + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 291a08f..04973b7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -6126,12 +6126,26 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data, conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { +#ifdef TIZEN_BT + if (ev->status) { + hci_dev_unlock(hdev); + mgmt_le_conn_update_failed(hdev, &conn->dst, + conn->type, conn->dst_type, ev->status); + return; + } +#endif conn->le_conn_interval = le16_to_cpu(ev->interval); conn->le_conn_latency = le16_to_cpu(ev->latency); conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout); } hci_dev_unlock(hdev); + +#ifdef TIZEN_BT + mgmt_le_conn_updated(hdev, &conn->dst, conn->type, + conn->dst_type, conn->le_conn_interval, + conn->le_conn_latency, conn->le_supv_timeout); +#endif } /* This function requires the caller holds hdev->lock */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 128f7bb..07e7c2c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -8575,6 +8575,74 @@ static int disable_le_auto_connect(struct sock *sk, struct hci_dev *hdev, return err; } + +static inline int check_le_conn_update_param(u16 min, u16 max, u16 latency, + u16 to_multiplier) +{ + u16 max_latency; + + if (min > max || min < 6 || max > 3200) + return -EINVAL; + + if (to_multiplier < 10 || to_multiplier > 3200) + return -EINVAL; + + if (max >= to_multiplier * 8) + return -EINVAL; + + max_latency = (to_multiplier * 8 / max) - 1; + + if (latency > 499 || latency > max_latency) + return -EINVAL; + + return 0; +} + +static int le_conn_update(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_le_conn_update *cp = data; + + struct hci_conn *conn; + u16 min, max, latency, supervision_timeout; + int err = -1; + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, + MGMT_STATUS_NOT_POWERED); + + min = __le16_to_cpu(cp->conn_interval_min); + max = __le16_to_cpu(cp->conn_interval_max); + latency = __le16_to_cpu(cp->conn_latency); + supervision_timeout = __le16_to_cpu(cp->supervision_timeout); + + BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x supervision_timeout: 0x%4.4x", + min, max, latency, supervision_timeout); + + err = check_le_conn_update_param(min, max, latency, + supervision_timeout); + + if (err < 0) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr); + if (!conn) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, + MGMT_STATUS_NOT_CONNECTED); + hci_dev_unlock(hdev); + return err; + } + + hci_dev_unlock(hdev); + + hci_le_conn_update(conn, min, max, latency, supervision_timeout); + + return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, 0, + NULL, 0); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -9508,6 +9576,35 @@ int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name, return mgmt_event(MGMT_EV_DEVICE_NAME_UPDATE, hdev, buf, sizeof(*ev) + eir_len, NULL); } + +int mgmt_le_conn_update_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status) +{ + struct mgmt_ev_conn_update_failed ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_bdaddr(link_type, addr_type); + ev.status = status; + + return mgmt_event(MGMT_EV_CONN_UPDATE_FAILED, hdev, + &ev, sizeof(ev), NULL); +} + +int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u16 conn_interval, + u16 conn_latency, u16 supervision_timeout) +{ + struct mgmt_ev_conn_updated ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_bdaddr(link_type, addr_type); + ev.conn_interval = cpu_to_le16(conn_interval); + ev.conn_latency = cpu_to_le16(conn_latency); + ev.supervision_timeout = cpu_to_le16(supervision_timeout); + + return mgmt_event(MGMT_EV_CONN_UPDATED, hdev, + &ev, sizeof(ev), NULL); +} #endif static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data, @@ -10834,6 +10931,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { start_le_discovery, MGMT_START_LE_DISCOVERY_SIZE }, { stop_le_discovery, MGMT_STOP_LE_DISCOVERY_SIZE }, { disable_le_auto_connect, MGMT_DISABLE_LE_AUTO_CONNECT_SIZE }, + { le_conn_update, MGMT_LE_CONN_UPDATE_SIZE }, }; #endif -- 2.7.4