From 95cfafa2a3a4adf0d29fa25fd78deaaed2897e06 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Wed, 24 Aug 2016 12:17:16 +0530 Subject: [PATCH 01/16] Bluetooth: Add Advertising Packet Configuration This patch provides new MGMT commands to configure the advertising data and scan response data packets for LE peripheral devices. Change-Id: I914d13795f4fb58e5f2e1cadb55086f4bcbc82df Signed-off-by: Sudha Bheemanna Signed-off-by: DoHyun Pyun Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 5 + include/net/bluetooth/mgmt_tizen.h | 23 ++++ net/bluetooth/hci_core.c | 4 + net/bluetooth/hci_request.c | 24 +++- net/bluetooth/mgmt.c | 224 +++++++++++++++++++++++++++++++++++++ 5 files changed, 279 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b47bf34..8a4e878 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -407,6 +407,11 @@ struct hci_dev { struct led_trigger *power_led; #endif +#ifdef TIZEN_BT + __u8 adv_filter_policy; + __u8 adv_type; +#endif + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 6bad3c9..04bd255 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -23,4 +23,27 @@ #define TIZEN_OP_CODE_BASE 0xff00 #define TIZEN_EV_BASE 0xff00 +#define MGMT_OP_SET_ADVERTISING_PARAMS (TIZEN_OP_CODE_BASE + 0x01) +struct mgmt_cp_set_advertising_params { + __le16 interval_min; + __le16 interval_max; + __u8 filter_policy; + __u8 type; +} __packed; +#define MGMT_SET_ADVERTISING_PARAMS_SIZE 6 + +#define MGMT_OP_SET_ADVERTISING_DATA (TIZEN_OP_CODE_BASE + 0x02) +struct mgmt_cp_set_advertising_data { + __u8 data[HCI_MAX_AD_LENGTH]; +} __packed; +#define MGMT_SET_ADVERTISING_DATA_SIZE HCI_MAX_AD_LENGTH +#define MGMT_SET_ADV_MIN_APP_DATA_SIZE 1 + +#define MGMT_OP_SET_SCAN_RSP_DATA (TIZEN_OP_CODE_BASE + 0x03) +struct mgmt_cp_set_scan_rsp_data { + __u8 data[HCI_MAX_AD_LENGTH]; +} __packed; +#define MGMT_SET_SCAN_RSP_DATA_SIZE HCI_MAX_AD_LENGTH +#define MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE 1 + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a70b078..447ee09 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2961,6 +2961,10 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_adv_channel_map = 0x07; hdev->le_adv_min_interval = 0x0800; hdev->le_adv_max_interval = 0x0800; +#ifdef TIZEN_BT + hdev->adv_filter_policy = 0x00; + hdev->adv_type = 0x00; +#endif hdev->le_scan_interval = 0x0060; hdev->le_scan_window = 0x0030; hdev->le_conn_min_interval = 0x0028; diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 4a89e12..3c4ca4b 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -829,6 +829,7 @@ void hci_req_add_le_passive_scan(struct hci_request *req) &enable_cp); } +#ifndef TIZEN_BT static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev) { u8 instance = hdev->cur_adv_instance; @@ -847,6 +848,7 @@ static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev) */ return adv_instance->scan_rsp_len; } +#endif void __hci_req_disable_advertising(struct hci_request *req) { @@ -954,13 +956,17 @@ void __hci_req_enable_advertising(struct hci_request *req) cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval); cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval); +#ifdef TIZEN_BT + cp.filter_policy = hdev->adv_filter_policy; + cp.type = hdev->adv_type; +#else if (connectable) cp.type = LE_ADV_IND; else if (get_cur_adv_instance_scan_rsp_len(hdev)) cp.type = LE_ADV_SCAN_IND; else cp.type = LE_ADV_NONCONN_IND; - +#endif cp.own_address_type = own_addr_type; cp.channel_map = hdev->le_adv_channel_map; @@ -1065,6 +1071,13 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance) len = create_instance_scan_rsp_data(hdev, instance, cp.data); else len = create_default_scan_rsp_data(hdev, cp.data); +#ifdef TIZEN_BT + /* Advertising scan response data is handled in bluez. + * This value will be updated only when application request the update + * using adapter_set_scan_rsp_data() + */ + return; +#else if (hdev->scan_rsp_data_len == len && !memcmp(cp.data, hdev->scan_rsp_data, len)) @@ -1076,6 +1089,7 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance) cp.length = len; hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp); +#endif } static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr) @@ -1168,6 +1182,13 @@ void __hci_req_update_adv_data(struct hci_request *req, u8 instance) len = create_instance_adv_data(hdev, instance, cp.data); +#ifdef TIZEN_BT + /* Bluez will handle the advertising data including the flag and tx + * power. This value will be updated only when application request the + * update using adapter_set_advertising_data(). + */ + return; +#else /* There's nothing to do if the data hasn't changed */ if (hdev->adv_data_len == len && memcmp(cp.data, hdev->adv_data, len) == 0) @@ -1179,6 +1200,7 @@ void __hci_req_update_adv_data(struct hci_request *req, u8 instance) cp.length = len; hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); +#endif } int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 53ec337..8164fc6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4714,6 +4714,225 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, return err; } +#ifdef TIZEN_BT +static int set_advertising_params(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_advertising_params *cp = data; + __u16 min_interval; + __u16 max_interval; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_PARAMS, + MGMT_STATUS_NOT_SUPPORTED); + + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_PARAMS, + MGMT_STATUS_BUSY); + + min_interval = __le16_to_cpu(cp->interval_min); + max_interval = __le16_to_cpu(cp->interval_max); + + if (min_interval > max_interval || + min_interval < 0x0020 || max_interval > 0x4000) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_PARAMS, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + hdev->le_adv_min_interval = min_interval; + hdev->le_adv_max_interval = max_interval; + hdev->adv_filter_policy = cp->filter_policy; + hdev->adv_type = cp->type; + + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_PARAMS, 0, NULL, 0); + + hci_dev_unlock(hdev); + + return err; +} + +static void set_advertising_data_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_cp_set_advertising_data *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_ADVERTISING_DATA, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, 0, + cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int set_advertising_data(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_set_advertising_data *cp = data; + struct hci_cp_le_set_adv_data adv; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) { + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, + MGMT_STATUS_NOT_SUPPORTED); + } + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_SET_ADVERTISING_DATA, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, + MGMT_STATUS_BUSY); + goto unlocked; + } + + if (len > HCI_MAX_AD_LENGTH) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_ADVERTISING_DATA, + MGMT_STATUS_INVALID_PARAMS); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING_DATA, + hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + memset(&adv, 0, sizeof(adv)); + memcpy(adv.data, cp->data, len); + adv.length = len; + + hci_req_add(&req, HCI_OP_LE_SET_ADV_DATA, sizeof(adv), &adv); + + err = hci_req_run(&req, set_advertising_data_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +static void set_scan_rsp_data_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_cp_set_scan_rsp_data *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_SCAN_RSP_DATA, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_SET_SCAN_RSP_DATA, 0, + cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int set_scan_rsp_data(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_set_scan_rsp_data *cp = data; + struct hci_cp_le_set_scan_rsp_data rsp; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_SCAN_RSP_DATA, + MGMT_STATUS_NOT_SUPPORTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_SET_SCAN_RSP_DATA, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA, + MGMT_STATUS_BUSY); + goto unlocked; + } + + if (len > HCI_MAX_AD_LENGTH) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA, + MGMT_STATUS_INVALID_PARAMS); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_SCAN_RSP_DATA, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + memset(&rsp, 0, sizeof(rsp)); + memcpy(rsp.data, cp->data, len); + rsp.length = len; + + hci_req_add(&req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(rsp), &rsp); + + err = hci_req_run(&req, set_scan_rsp_data_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlocked: + hci_dev_unlock(hdev); + + return err; +} +#endif /* TIZEN_BT */ + static bool ltk_is_valid(struct mgmt_ltk_info *key) { if (key->master != 0x00 && key->master != 0x01) @@ -6548,6 +6767,11 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { #ifdef TIZEN_BT static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ + { set_advertising_params, MGMT_SET_ADVERTISING_PARAMS_SIZE }, + { set_advertising_data, MGMT_SET_ADV_MIN_APP_DATA_SIZE, + HCI_MGMT_VAR_LEN }, + { set_scan_rsp_data, MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE, + HCI_MGMT_VAR_LEN }, }; #endif -- 2.7.4 From cb7d0762bd6445d39383f043e9c6c6329860c200 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Wed, 24 Aug 2016 15:02:56 +0530 Subject: [PATCH 02/16] Bluetooth: Functions to modify WhiteList This patch provides MGMT commands to manage the white list which includes, adding, removing and clearing the devices from white list. Change-Id: If71107129d3a090ae81448a8122b76accd4f5522 Signed-off-by: Sudha Bheemanna Signed-off-by: DoHyun Pyun Signed-off-by: Amit Purwar --- include/net/bluetooth/mgmt_tizen.h | 17 +++ net/bluetooth/mgmt.c | 241 +++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 04bd255..6dc8e36 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -46,4 +46,21 @@ struct mgmt_cp_set_scan_rsp_data { #define MGMT_SET_SCAN_RSP_DATA_SIZE HCI_MAX_AD_LENGTH #define MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE 1 +#define MGMT_OP_ADD_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x04) +struct mgmt_cp_add_dev_white_list { + __u8 bdaddr_type; + bdaddr_t bdaddr; +} __packed; +#define MGMT_ADD_DEV_WHITE_LIST_SIZE 7 + +#define MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x05) +struct mgmt_cp_remove_dev_from_white_list { + __u8 bdaddr_type; + bdaddr_t bdaddr; +} __packed; +#define MGMT_REMOVE_DEV_FROM_WHITE_LIST_SIZE 7 + +#define MGMT_OP_CLEAR_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x06) +#define MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE 0 + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8164fc6..c953154 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4931,6 +4931,244 @@ unlocked: return err; } + +/* Adv White List feature */ +static void add_white_list_complete(struct hci_dev *hdev, u8 status, u16 opcode) +{ + struct mgmt_cp_add_dev_white_list *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_ADD_DEV_WHITE_LIST, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_ADD_DEV_WHITE_LIST, 0, cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int add_white_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct mgmt_cp_add_dev_white_list *cp = data; + struct hci_request req; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST, + MGMT_STATUS_NOT_SUPPORTED); + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST, + MGMT_STATUS_REJECTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_ADD_DEV_WHITE_LIST, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEV_WHITE_LIST, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(*cp), cp); + + err = hci_req_run(&req, add_white_list_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +static void remove_from_white_list_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_cp_remove_dev_from_white_list *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, 0, + cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int remove_from_white_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct mgmt_cp_remove_dev_from_white_list *cp = data; + struct hci_request req; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + MGMT_STATUS_NOT_SUPPORTED); + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + MGMT_STATUS_REJECTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, + hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_LE_DEL_FROM_WHITE_LIST, sizeof(*cp), cp); + + err = hci_req_run(&req, remove_from_white_list_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +static void clear_white_list_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_CLEAR_DEV_WHITE_LIST, hdev); + if (!cmd) + goto unlock; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_CLEAR_DEV_WHITE_LIST, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_CLEAR_DEV_WHITE_LIST, + 0, NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int clear_white_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_CLEAR_DEV_WHITE_LIST, + MGMT_STATUS_NOT_SUPPORTED); + + if (!hdev_is_powered(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_CLEAR_DEV_WHITE_LIST, + MGMT_STATUS_REJECTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_CLEAR_DEV_WHITE_LIST, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_CLEAR_DEV_WHITE_LIST, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_CLEAR_DEV_WHITE_LIST, + hdev, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_LE_CLEAR_WHITE_LIST, 0, NULL); + + err = hci_req_run(&req, clear_white_list_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -6772,6 +7010,9 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { HCI_MGMT_VAR_LEN }, { set_scan_rsp_data, MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE, HCI_MGMT_VAR_LEN }, + { add_white_list, MGMT_ADD_DEV_WHITE_LIST_SIZE }, + { remove_from_white_list, MGMT_REMOVE_DEV_FROM_WHITE_LIST_SIZE }, + { clear_white_list, MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE }, }; #endif -- 2.7.4 From 82a1d935e5b526abde587cacf815e4a36b95f9dd Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Wed, 24 Aug 2016 17:21:15 +0530 Subject: [PATCH 03/16] Bluetooth: Add RSSI Monitor feature Added feature support for monitoring the RSSI value. Commands and events for enabling, disabling and setting rssi threshold values are added. Change-Id: I850643a9228afc017e54217a11826b9c6a68a96b Signed-off-by: Sudha Bheemanna Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci.h | 54 +++ include/net/bluetooth/hci_core.h | 80 +++++ include/net/bluetooth/mgmt_tizen.h | 63 ++++ net/bluetooth/hci_event.c | 32 ++ net/bluetooth/mgmt.c | 686 +++++++++++++++++++++++++++++++++++++ 5 files changed, 915 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 99aa5e5..5ce0292 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1498,6 +1498,60 @@ struct hci_rp_le_read_max_data_len { __le16 rx_time; } __packed; +#ifdef TIZEN_BT +/* + * Vendor Specific HCI Command + * Vendor: Broadcom + * Purpose: This HCI is used to enable RSSI monitoring and setting + * Threshold Values for LE Link + */ +#define HCI_OP_ENABLE_RSSI 0xfce9 + +struct hci_cp_set_enable_rssi { + __u8 hci_le_ext_opcode; + __u8 le_enable_cs_Features; + __u8 data[3]; +} __packed; + +struct hci_cp_set_rssi_threshold { + __u8 hci_le_ext_opcode; + __u8 mode; + __le16 conn_handle; + __u8 alert_mask; + __u8 low_th; + __u8 in_range_th; + __u8 high_th; +} __packed; + +struct hci_cc_rsp_enable_rssi { + __u8 status; + __u8 le_ext_opcode; +} __packed; + +struct hci_ev_vendor_specific_rssi_alert { + __le16 conn_handle; + __s8 alert_type; + __s8 rssi_dbm; +} __packed; + +/* + * Vendor Specific HCI Command + * Vendor: Broadcom + * Purpose: This HCI is used to get Raw RSSI value for a Link + */ +#define HCI_OP_GET_RAW_RSSI 0xfc48 + +struct hci_cp_get_raw_rssi { + __le16 conn_handle; +} __packed; + +struct hci_cc_rp_get_raw_rssi { + __u8 status; + __le16 conn_handle; + __s8 rssi_dbm; +} __packed; +#endif + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8a4e878..f088d22 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -503,6 +503,9 @@ struct hci_conn { void *sco_data; struct amp_mgr *amp_mgr; +#ifdef TIZEN_BT + bool rssi_monitored; +#endif struct hci_conn *link; void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); @@ -881,6 +884,70 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev) return NULL; } +#ifdef TIZEN_BT +static inline bool hci_conn_rssi_state_set(struct hci_dev *hdev, + __u8 type, bdaddr_t *ba, bool value) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *c; + __u8 conn_type; + + if (type == 0x01) + conn_type = LE_LINK; + else + conn_type = ACL_LINK; + + rcu_read_lock(); + + list_for_each_entry_rcu(c, &h->list, list) { + if (c->type == conn_type && !bacmp(&c->dst, ba)) { + c->rssi_monitored = value; + rcu_read_unlock(); + return true; + } + } + + rcu_read_unlock(); + return false; +} + +static inline void hci_conn_rssi_unset_all(struct hci_dev *hdev, + __u8 type) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *c; + __u8 conn_type; + + if (type == 0x01) + conn_type = LE_LINK; + else + conn_type = ACL_LINK; + + rcu_read_lock(); + list_for_each_entry_rcu(c, &h->list, list) { + if (c->type == conn_type) + c->rssi_monitored = false; + } + rcu_read_unlock(); +} + +static inline int hci_conn_hash_lookup_rssi_count(struct hci_dev *hdev) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *c; + int count = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(c, &h->list, list) { + if (c->rssi_monitored == true) + ++count; + } + rcu_read_unlock(); + + return count; +} +#endif + int hci_disconnect(struct hci_conn *conn, __u8 reason); bool hci_setup_sync(struct hci_conn *conn, __u16 handle); void hci_sco_setup(struct hci_conn *conn, __u8 status); @@ -1556,6 +1623,19 @@ void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev, void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev, u8 instance); +#ifdef TIZEN_BT +void mgmt_rssi_enable_success(struct sock *sk, struct hci_dev *hdev, + void *data, struct hci_cc_rsp_enable_rssi *rp, int success); +void mgmt_rssi_disable_success(struct sock *sk, struct hci_dev *hdev, + void *data, struct hci_cc_rsp_enable_rssi *rp, int success); +int mgmt_set_rssi_threshold(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len); +void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb); +void mgmt_raw_rssi_response(struct hci_dev *hdev, + struct hci_cc_rp_get_raw_rssi *rp, int success); +void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status); +#endif + u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 6dc8e36..46f652b 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -63,4 +63,67 @@ struct mgmt_cp_remove_dev_from_white_list { #define MGMT_OP_CLEAR_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x06) #define MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE 0 +/* For RSSI monitoring */ +#define MGMT_OP_SET_RSSI_ENABLE (TIZEN_OP_CODE_BASE + 0x07) +#define MGMT_SET_RSSI_ENABLE_SIZE 10 + +struct mgmt_cp_set_enable_rssi { + __s8 low_th; + __s8 in_range_th; + __s8 high_th; + bdaddr_t bdaddr; + __s8 link_type; +} __packed; + +struct mgmt_cc_rsp_enable_rssi { + __u8 status; + __u8 le_ext_opcode; + bdaddr_t bt_address; + __s8 link_type; +} __packed; + +#define MGMT_OP_GET_RAW_RSSI (TIZEN_OP_CODE_BASE + 0x08) +#define MGMT_GET_RAW_RSSI_SIZE 7 + +struct mgmt_cp_get_raw_rssi { + bdaddr_t bt_address; + __u8 link_type; +} __packed; + +#define MGMT_OP_SET_RSSI_DISABLE (TIZEN_OP_CODE_BASE + 0x09) +#define MGMT_SET_RSSI_DISABLE_SIZE 7 +struct mgmt_cp_disable_rssi { + bdaddr_t bdaddr; + __u8 link_type; +} __packed; +struct mgmt_cc_rp_disable_rssi { + __u8 status; + __u8 le_ext_opcode; + bdaddr_t bt_address; + __s8 link_type; +} __packed; +/* RSSI monitoring */ + +/* For handling of RSSI Events */ +#define MGMT_EV_RSSI_ALERT (TIZEN_EV_BASE + 0x04) +struct mgmt_ev_vendor_specific_rssi_alert { + bdaddr_t bdaddr; + __s8 link_type; + __s8 alert_type; + __s8 rssi_dbm; +} __packed; + +#define MGMT_EV_RAW_RSSI (TIZEN_EV_BASE + 0x05) +struct mgmt_cc_rp_get_raw_rssi { + __u8 status; + __s8 rssi_dbm; + __u8 link_type; + bdaddr_t bt_address; +} __packed; + +#define MGMT_EV_RSSI_ENABLED (TIZEN_EV_BASE + 0x06) + +#define MGMT_EV_RSSI_DISABLED (TIZEN_EV_BASE + 0x07) +/* Handling of RSSI Events */ + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 1f5c48d..1d22235 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1392,6 +1392,30 @@ static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); } +#ifdef TIZEN_BT +static void hci_cc_enable_rssi(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cc_rsp_enable_rssi *rp = (void *)skb->data; + + BT_DBG("hci_cc_enable_rssi - %s status 0x%2.2x Event_LE_ext_Opcode 0x%2.2x", + hdev->name, rp->status, rp->le_ext_opcode); + + mgmt_enable_rssi_cc(hdev, rp, rp->status); +} + +static void hci_cc_get_raw_rssi(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cc_rp_get_raw_rssi *rp = (void *)skb->data; + + BT_DBG("hci_cc_get_raw_rssi- %s Get Raw Rssi Response[%2.2x %4.4x %2.2X]", + hdev->name, rp->status, rp->conn_handle, rp->rssi_dbm); + + mgmt_raw_rssi_response(hdev, rp, rp->status); +} +#endif + static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_rssi *rp = (void *) skb->data; @@ -3030,7 +3054,15 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb, case HCI_OP_WRITE_SSP_DEBUG_MODE: hci_cc_write_ssp_debug_mode(hdev, skb); break; +#ifdef TIZEN_BT + case HCI_OP_ENABLE_RSSI: + hci_cc_enable_rssi(hdev, skb); + break; + case HCI_OP_GET_RAW_RSSI: + hci_cc_get_raw_rssi(hdev, skb); + break; +#endif default: BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c953154..95f81c0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5169,6 +5169,689 @@ unlocked: return err; } + +static void set_rssi_threshold_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + if (!cmd) + goto unlock; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, 0, + NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static void set_rssi_disable_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + if (!cmd) + goto unlock; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + 0, NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +int mgmt_set_rssi_threshold(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + int err = 0; + struct hci_cp_set_rssi_threshold th = { 0, }; + struct mgmt_cp_set_enable_rssi *cp = data; + struct hci_conn *conn; + struct mgmt_pending_cmd *cmd; + struct hci_request req; + __u8 dest_type; + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + if (!cmd) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_FAILED); + goto unlocked; + } + + if (!lmp_le_capable(hdev)) { + mgmt_pending_remove(cmd); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + mgmt_pending_remove(cmd); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + if (cp->link_type == 0x01) + dest_type = LE_LINK; + else + dest_type = ACL_LINK; + + /* Get LE/ACL link handle info */ + conn = hci_conn_hash_lookup_ba(hdev, + dest_type, &cp->bdaddr); + + if (!conn) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_SET_RSSI_ENABLE, 1, NULL, 0); + mgmt_pending_remove(cmd); + goto unlocked; + } + + hci_req_init(&req, hdev); + + th.hci_le_ext_opcode = 0x0B; + th.mode = 0x01; + th.conn_handle = conn->handle; + th.alert_mask = 0x07; + th.low_th = cp->low_th; + th.in_range_th = cp->in_range_th; + th.high_th = cp->high_th; + + hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(th), &th); + err = hci_req_run(&req, set_rssi_threshold_complete); + + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + return err; +} + +void mgmt_rssi_enable_success(struct sock *sk, struct hci_dev *hdev, + void *data, struct hci_cc_rsp_enable_rssi *rp, int success) +{ + struct mgmt_cc_rsp_enable_rssi mgmt_rp = { 0, }; + struct mgmt_cp_set_enable_rssi *cp = data; + struct mgmt_pending_cmd *cmd; + + if (!cp || !rp) + goto remove_cmd; + + mgmt_rp.status = rp->status; + mgmt_rp.le_ext_opcode = rp->le_ext_opcode; + mgmt_rp.bt_address = cp->bdaddr; + mgmt_rp.link_type = cp->link_type; + + mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_SUCCESS, &mgmt_rp, + sizeof(struct mgmt_cc_rsp_enable_rssi)); + + mgmt_event(MGMT_EV_RSSI_ENABLED, hdev, &mgmt_rp, + sizeof(struct mgmt_cc_rsp_enable_rssi), NULL); + + hci_conn_rssi_unset_all(hdev, mgmt_rp.link_type); + hci_conn_rssi_state_set(hdev, mgmt_rp.link_type, + &mgmt_rp.bt_address, true); + +remove_cmd: + hci_dev_lock(hdev); + cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + if (cmd) + mgmt_pending_remove(cmd); + + hci_dev_unlock(hdev); +} + +void mgmt_rssi_disable_success(struct sock *sk, struct hci_dev *hdev, + void *data, struct hci_cc_rsp_enable_rssi *rp, int success) +{ + struct mgmt_cc_rp_disable_rssi mgmt_rp = { 0, }; + struct mgmt_cp_disable_rssi *cp = data; + struct mgmt_pending_cmd *cmd; + + if (!cp || !rp) + goto remove_cmd; + + mgmt_rp.status = rp->status; + mgmt_rp.le_ext_opcode = rp->le_ext_opcode; + mgmt_rp.bt_address = cp->bdaddr; + mgmt_rp.link_type = cp->link_type; + + mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_SUCCESS, &mgmt_rp, + sizeof(struct mgmt_cc_rsp_enable_rssi)); + + mgmt_event(MGMT_EV_RSSI_DISABLED, hdev, &mgmt_rp, + sizeof(struct mgmt_cc_rsp_enable_rssi), NULL); + + hci_conn_rssi_state_set(hdev, mgmt_rp.link_type, + &mgmt_rp.bt_address, false); + +remove_cmd: + hci_dev_lock(hdev); + cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + if (cmd) + mgmt_pending_remove(cmd); + + hci_dev_unlock(hdev); +} + +static int mgmt_set_disable_rssi(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct hci_cp_set_enable_rssi cp_en = { 0, }; + int err; + + BT_DBG("Set Disable RSSI."); + + cp_en.hci_le_ext_opcode = 0x01; + cp_en.le_enable_cs_Features = 0x00; + cp_en.data[0] = 0x00; + cp_en.data[1] = 0x00; + cp_en.data[2] = 0x00; + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + if (!cmd) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_FAILED); + goto unlocked; + } + + if (!lmp_le_capable(hdev)) { + mgmt_pending_remove(cmd); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + mgmt_pending_remove(cmd); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + hci_req_init(&req, hdev); + + BT_DBG("Enable Len: %zu [%2.2X %2.2X %2.2X %2.2X %2.2X]", + sizeof(struct hci_cp_set_enable_rssi), + cp_en.hci_le_ext_opcode, cp_en.le_enable_cs_Features, + cp_en.data[0], cp_en.data[1], cp_en.data[2]); + + hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(cp_en), &cp_en); + err = hci_req_run(&req, set_rssi_disable_complete); + + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + return err; +} + +void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status) +{ + struct hci_cc_rsp_enable_rssi *rp = response; + struct mgmt_pending_cmd *cmd_enable = NULL; + struct mgmt_pending_cmd *cmd_disable = NULL; + struct mgmt_cp_set_enable_rssi *cp_en; + struct mgmt_cp_disable_rssi *cp_dis; + + hci_dev_lock(hdev); + cmd_enable = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + cmd_disable = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + hci_dev_unlock(hdev); + + if (cmd_enable) + BT_DBG("Enable Request"); + + if (cmd_disable) + BT_DBG("Disable Request"); + + if (cmd_enable) { + cp_en = cmd_enable->param; + + if (status != 0x00) + return; + + switch (rp->le_ext_opcode) { + case 0x01: + BT_DBG("RSSI enabled.. Setting Threshold..."); + mgmt_set_rssi_threshold(cmd_enable->sk, hdev, + cp_en, sizeof(*cp_en)); + break; + + case 0x0B: + BT_DBG("Sending RSSI enable success"); + mgmt_rssi_enable_success(cmd_enable->sk, hdev, + cp_en, rp, rp->status); + break; + } + + } else if (cmd_disable) { + cp_dis = cmd_disable->param; + + if (status != 0x00) + return; + + switch (rp->le_ext_opcode) { + case 0x01: + BT_DBG("Sending RSSI disable success"); + mgmt_rssi_disable_success(cmd_disable->sk, hdev, + cp_dis, rp, rp->status); + break; + + case 0x0B: + /* + * Only unset RSSI Threshold values for the Link if + * RSSI is monitored for other BREDR or LE Links + */ + if (hci_conn_hash_lookup_rssi_count(hdev) > 1) { + BT_DBG("Unset Threshold. Other links being monitored"); + mgmt_rssi_disable_success(cmd_disable->sk, hdev, + cp_dis, rp, rp->status); + } else { + BT_DBG("Unset Threshold. Disabling..."); + mgmt_set_disable_rssi(cmd_disable->sk, hdev, + cp_dis, sizeof(*cp_dis)); + } + break; + } + } +} + +static void set_rssi_enable_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev); + if (!cmd) + goto unlock; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, 0, + NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int set_enable_rssi(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_set_enable_rssi *cp = data; + struct hci_cp_set_enable_rssi cp_en = { 0, }; + int err; + + BT_DBG("Set Enable RSSI."); + + cp_en.hci_le_ext_opcode = 0x01; + cp_en.le_enable_cs_Features = 0x04; + cp_en.data[0] = 0x00; + cp_en.data[1] = 0x00; + cp_en.data[2] = 0x00; + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + if (pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_RSSI_ENABLE, hdev, cp, + sizeof(*cp)); + if (!cmd) { + BT_DBG("%s", hdev->name); + err = -ENOMEM; + goto unlocked; + } + + /* If RSSI is already enabled directly set Threshold values */ + if (hci_conn_hash_lookup_rssi_count(hdev) > 0) { + hci_dev_unlock(hdev); + BT_DBG("RSSI Enabled. Directly set Threshold"); + err = mgmt_set_rssi_threshold(sk, hdev, cp, sizeof(*cp)); + return err; + } + + hci_req_init(&req, hdev); + + BT_DBG("Enable Len: %zu [%2.2X %2.2X %2.2X %2.2X %2.2X]", + sizeof(struct hci_cp_set_enable_rssi), + cp_en.hci_le_ext_opcode, cp_en.le_enable_cs_Features, + cp_en.data[0], cp_en.data[1], cp_en.data[2]); + + hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(cp_en), &cp_en); + err = hci_req_run(&req, set_rssi_enable_complete); + + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +static void get_raw_rssi_complete(struct hci_dev *hdev, u8 status, u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_GET_RAW_RSSI, hdev); + if (!cmd) + goto unlock; + + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_SUCCESS, &status, 1); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int get_raw_rssi(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_get_raw_rssi *cp = data; + struct hci_cp_get_raw_rssi hci_cp; + + struct hci_conn *conn; + int err; + __u8 dest_type; + + BT_DBG("Get Raw RSSI."); + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (cp->link_type == 0x01) + dest_type = LE_LINK; + else + dest_type = ACL_LINK; + + /* Get LE/BREDR link handle info */ + conn = hci_conn_hash_lookup_ba(hdev, + dest_type, &cp->bt_address); + if (!conn) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_NOT_CONNECTED); + goto unlocked; + } + hci_cp.conn_handle = conn->handle; + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + if (pending_find(MGMT_OP_GET_RAW_RSSI, hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_GET_RAW_RSSI, hdev, data, len); + if (!cmd) { + BT_DBG("%s", hdev->name); + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + BT_DBG("Connection Handle [%d]", hci_cp.conn_handle); + hci_req_add(&req, HCI_OP_GET_RAW_RSSI, sizeof(hci_cp), &hci_cp); + err = hci_req_run(&req, get_raw_rssi_complete); + + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +void mgmt_raw_rssi_response(struct hci_dev *hdev, + struct hci_cc_rp_get_raw_rssi *rp, int success) +{ + struct mgmt_cc_rp_get_raw_rssi mgmt_rp = { 0, }; + struct hci_conn *conn; + + mgmt_rp.status = rp->status; + mgmt_rp.rssi_dbm = rp->rssi_dbm; + + conn = hci_conn_hash_lookup_handle(hdev, rp->conn_handle); + if (!conn) + return; + + bacpy(&mgmt_rp.bt_address, &conn->dst); + if (conn->type == LE_LINK) + mgmt_rp.link_type = 0x01; + else + mgmt_rp.link_type = 0x00; + + mgmt_event(MGMT_EV_RAW_RSSI, hdev, &mgmt_rp, + sizeof(struct mgmt_cc_rp_get_raw_rssi), NULL); +} + +static void set_disable_threshold_complete(struct hci_dev *hdev, + u8 status, u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev); + if (!cmd) + goto unlock; + + mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_SUCCESS, &status, 1); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +/** Removes monitoring for a link*/ +static int set_disable_threshold(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + int err = 0; + struct hci_cp_set_rssi_threshold th = { 0, }; + struct mgmt_cp_disable_rssi *cp = data; + struct hci_conn *conn; + struct mgmt_pending_cmd *cmd; + struct hci_request req; + __u8 dest_type; + + BT_DBG("Set Disable RSSI."); + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + /* Get LE/ACL link handle info*/ + if (cp->link_type == 0x01) + dest_type = LE_LINK; + else + dest_type = ACL_LINK; + + conn = hci_conn_hash_lookup_ba(hdev, dest_type, &cp->bdaddr); + if (!conn) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_SET_RSSI_DISABLE, 1, NULL, 0); + goto unlocked; + } + + th.hci_le_ext_opcode = 0x0B; + th.mode = 0x01; + th.conn_handle = conn->handle; + th.alert_mask = 0x00; + th.low_th = 0x00; + th.in_range_th = 0x00; + th.high_th = 0x00; + + if (!hdev_is_powered(hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + 0, data, len); + goto unlocked; + } + + if (pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev)) { + BT_DBG("%s", hdev->name); + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_RSSI_DISABLE, hdev, cp, + sizeof(*cp)); + if (!cmd) { + BT_DBG("%s", hdev->name); + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(th), &th); + err = hci_req_run(&req, set_disable_threshold_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + BT_ERR("Error in requesting hci_req_run"); + goto unlocked; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; +} + +void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_vendor_specific_rssi_alert *ev = (void *)skb->data; + struct mgmt_ev_vendor_specific_rssi_alert mgmt_ev; + struct hci_conn *conn; + + BT_DBG("RSSI alert [%2.2X %2.2X %2.2X]", + ev->conn_handle, ev->alert_type, ev->rssi_dbm); + + conn = hci_conn_hash_lookup_handle(hdev, ev->conn_handle); + + if (!conn) { + BT_ERR("RSSI alert Error: Device not found for handle"); + return; + } + bacpy(&mgmt_ev.bdaddr, &conn->dst); + + if (conn->type == LE_LINK) + mgmt_ev.link_type = 0x01; + else + mgmt_ev.link_type = 0x00; + + mgmt_ev.alert_type = ev->alert_type; + mgmt_ev.rssi_dbm = ev->rssi_dbm; + + mgmt_event(MGMT_EV_RSSI_ALERT, hdev, &mgmt_ev, + sizeof(struct mgmt_ev_vendor_specific_rssi_alert), + NULL); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -7013,6 +7696,9 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { add_white_list, MGMT_ADD_DEV_WHITE_LIST_SIZE }, { remove_from_white_list, MGMT_REMOVE_DEV_FROM_WHITE_LIST_SIZE }, { clear_white_list, MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE }, + { set_enable_rssi, MGMT_SET_RSSI_ENABLE_SIZE }, + { get_raw_rssi, MGMT_GET_RAW_RSSI_SIZE }, + { set_disable_threshold, MGMT_SET_RSSI_DISABLE_SIZE }, }; #endif -- 2.7.4 From 3a1dee76ec156011082f47fa581bf2e7ce03aeea Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 10:17:19 +0530 Subject: [PATCH 04/16] Bluetooth: Update device name on remote_name_event This patch updates the device name on receiving the HCI event remote_name_event during connection establishment. Change-Id: I9a217e6760b1803a70af201a3f6903e722079749 Signed-off-by: Sudha Bheemanna Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 2 ++ include/net/bluetooth/mgmt_tizen.h | 11 +++++++++++ net/bluetooth/hci_event.c | 10 ++++++++++ net/bluetooth/mgmt.c | 24 ++++++++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f088d22..1352198 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1634,6 +1634,8 @@ void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb); void mgmt_raw_rssi_response(struct hci_dev *hdev, struct hci_cc_rp_get_raw_rssi *rp, int success); 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); #endif u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 46f652b..102faf0 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -104,6 +104,17 @@ struct mgmt_cc_rp_disable_rssi { } __packed; /* RSSI monitoring */ +/* EVENTS */ + +/* For device name update changes */ +#define MGMT_EV_DEVICE_NAME_UPDATE (TIZEN_EV_BASE + 0x01) +struct mgmt_ev_device_name_update { + struct mgmt_addr_info addr; + __le16 eir_len; + __u8 eir[0]; +} __packed; +/* Device name update changes */ + /* For handling of RSSI Events */ #define MGMT_EV_RSSI_ALERT (TIZEN_EV_BASE + 0x04) struct mgmt_ev_vendor_specific_rssi_alert { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 1d22235..902f626 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1686,15 +1686,25 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, struct discovery_state *discov = &hdev->discovery; struct inquiry_entry *e; +#ifdef TIZEN_BT /* Update the mgmt connected state if necessary. Be careful with * conn objects that exist but are not (yet) connected however. * Only those in BT_CONFIG or BT_CONNECTED states can be * considered connected. */ if (conn && + (conn->state == BT_CONFIG || conn->state == BT_CONNECTED)) { + if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) + mgmt_device_connected(hdev, conn, 0, name, name_len); + else + mgmt_device_name_update(hdev, bdaddr, name, name_len); + } +#else + if (conn && (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, conn, 0, name, name_len); +#endif if (discov->state == DISCOVERY_STOPPED) return; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 95f81c0..db4270b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6801,6 +6801,30 @@ unlock: return err; } +#ifdef TIZEN_BT +int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name, + u8 name_len) +{ + char buf[512]; + struct mgmt_ev_device_name_update *ev = (void *)buf; + u16 eir_len = 0; + + if (name_len <= 0) + return -EINVAL; + + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = BDADDR_BREDR; + + eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, + name_len); + + ev->eir_len = cpu_to_le16(eir_len); + + return mgmt_event(MGMT_EV_DEVICE_NAME_UPDATE, hdev, buf, + sizeof(*ev) + eir_len, NULL); +} +#endif + static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status, u16 opcode, struct sk_buff *skb) { -- 2.7.4 From 90a18489cbbb7100ff1ecc06a990f9a4b772a440 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 11:23:02 +0530 Subject: [PATCH 05/16] Bluetooth: Add BT LE discovery feature This patch adds new MGMT commands to start LE discovery separately and handles LE discovery state. Change-Id: I85958b8c2b5c7e28f57c69e86037ab1e61a75db0 Signed-off-by: Sudha Bheemanna Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 7 + include/net/bluetooth/mgmt_tizen.h | 14 ++ net/bluetooth/hci_core.c | 45 +++++++ net/bluetooth/hci_event.c | 4 + net/bluetooth/mgmt.c | 269 +++++++++++++++++++++++++++++++++++++ 5 files changed, 339 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1352198..3071066 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -357,6 +357,9 @@ struct hci_dev { void *smp_bredr_data; struct discovery_state discovery; +#ifdef TIZEN_BT + struct discovery_state le_discovery; +#endif struct hci_conn_hash conn_hash; struct list_head mgmt_pending; @@ -946,6 +949,9 @@ static inline int hci_conn_hash_lookup_rssi_count(struct hci_dev *hdev) return count; } + +bool hci_le_discovery_active(struct hci_dev *hdev); +void hci_le_discovery_set_state(struct hci_dev *hdev, int state); #endif int hci_disconnect(struct hci_conn *conn, __u8 reason); @@ -1636,6 +1642,7 @@ void mgmt_raw_rssi_response(struct hci_dev *hdev, 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); #endif u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 102faf0..844af75 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -104,6 +104,20 @@ struct mgmt_cc_rp_disable_rssi { } __packed; /* RSSI monitoring */ +/* For le discovery */ +#define MGMT_OP_START_LE_DISCOVERY (TIZEN_OP_CODE_BASE + 0x0a) +struct mgmt_cp_start_le_discovery { + __u8 type; +} __packed; +#define MGMT_START_LE_DISCOVERY_SIZE 1 + +#define MGMT_OP_STOP_LE_DISCOVERY (TIZEN_OP_CODE_BASE + 0x0b) +struct mgmt_cp_stop_le_discovery { + __u8 type; +} __packed; +#define MGMT_STOP_LE_DISCOVERY_SIZE 1 +/* le discovery */ + /* EVENTS */ /* For device name update changes */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 447ee09..ae9d874 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -993,6 +993,51 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) } } +#ifdef TIZEN_BT +bool hci_le_discovery_active(struct hci_dev *hdev) +{ + struct discovery_state *discov = &hdev->le_discovery; + + switch (discov->state) { + case DISCOVERY_FINDING: + case DISCOVERY_RESOLVING: + return true; + + default: + return false; + } +} + +void hci_le_discovery_set_state(struct hci_dev *hdev, int state) +{ + BT_DBG("%s state %u -> %u", hdev->name, + hdev->le_discovery.state, state); + + if (hdev->le_discovery.state == state) + return; + + switch (state) { + case DISCOVERY_STOPPED: + hci_update_background_scan(hdev); + + if (hdev->le_discovery.state != DISCOVERY_STARTING) + mgmt_le_discovering(hdev, 0); + break; + case DISCOVERY_STARTING: + break; + case DISCOVERY_FINDING: + mgmt_le_discovering(hdev, 1); + break; + case DISCOVERY_RESOLVING: + break; + case DISCOVERY_STOPPING: + break; + } + + hdev->le_discovery.state = state; +} +#endif + void hci_inquiry_cache_flush(struct hci_dev *hdev) { struct discovery_state *cache = &hdev->discovery; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 902f626..25b89e7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1198,7 +1198,11 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, * re-enable it again if necessary. */ if (hci_dev_test_and_clear_flag(hdev, HCI_LE_SCAN_INTERRUPTED)) +#ifndef TIZEN_BT /* The below line is kernel bug. */ hci_discovery_set_state(hdev, DISCOVERY_STOPPED); +#else + hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED); +#endif else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) && hdev->discovery.state == DISCOVERY_FINDING) hci_req_reenable_advertising(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index db4270b..21edaeb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5852,6 +5852,273 @@ void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb) sizeof(struct mgmt_ev_vendor_specific_rssi_alert), NULL); } + +static int mgmt_start_le_discovery_failed(struct hci_dev *hdev, u8 status) +{ + struct mgmt_pending_cmd *cmd; + u8 type; + int err; + + hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED); + + cmd = pending_find(MGMT_OP_START_LE_DISCOVERY, hdev); + if (!cmd) + return -ENOENT; + + type = hdev->le_discovery.type; + + err = mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode, + mgmt_status(status), &type, sizeof(type)); + mgmt_pending_remove(cmd); + + return err; +} + +static void start_le_discovery_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + unsigned long timeout = 0; + + BT_DBG("status %d", status); + + if (status) { + hci_dev_lock(hdev); + mgmt_start_le_discovery_failed(hdev, status); + hci_dev_unlock(hdev); + return; + } + + hci_dev_lock(hdev); + hci_le_discovery_set_state(hdev, DISCOVERY_FINDING); + hci_dev_unlock(hdev); + + if (hdev->le_discovery.type != DISCOV_TYPE_LE) + BT_ERR("Invalid discovery type %d", hdev->le_discovery.type); + + if (!timeout) + return; + + queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout); +} + +static int start_le_discovery(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_start_le_discovery *cp = data; + struct mgmt_pending_cmd *cmd; + struct hci_cp_le_set_scan_param param_cp; + struct hci_cp_le_set_scan_enable enable_cp; + struct hci_request req; + u8 status, own_addr_type; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + MGMT_STATUS_NOT_POWERED); + goto unlock; + } + + if (hdev->le_discovery.state != DISCOVERY_STOPPED) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + MGMT_STATUS_BUSY); + goto unlock; + } + + if (cp->type != DISCOV_TYPE_LE) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_START_LE_DISCOVERY, hdev, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hdev->le_discovery.type = cp->type; + + hci_req_init(&req, hdev); + + status = mgmt_le_support(hdev); + if (status) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + status); + mgmt_pending_remove(cmd); + goto unlock; + } + + /* If controller is scanning, it means the background scanning + * is running. Thus, we should temporarily stop it in order to + * set the discovery scanning parameters. + */ + if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) + hci_req_add_le_scan_disable(&req); + + memset(¶m_cp, 0, sizeof(param_cp)); + + /* All active scans will be done with either a resolvable + * private address (when privacy feature has been enabled) + * or unresolvable private address. + */ + err = hci_update_random_address(&req, true, hci_dev_test_flag(hdev, HCI_PRIVACY), &own_addr_type); + if (err < 0) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY, + MGMT_STATUS_FAILED); + mgmt_pending_remove(cmd); + goto unlock; + } + + param_cp.type = hdev->le_scan_type; + param_cp.interval = cpu_to_le16(hdev->le_scan_interval); + param_cp.window = cpu_to_le16(hdev->le_scan_window); + param_cp.own_address_type = own_addr_type; + hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), + ¶m_cp); + + memset(&enable_cp, 0, sizeof(enable_cp)); + enable_cp.enable = LE_SCAN_ENABLE; + enable_cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE; + + hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), + &enable_cp); + + err = hci_req_run(&req, start_le_discovery_complete); + if (err < 0) + mgmt_pending_remove(cmd); + else + hci_le_discovery_set_state(hdev, DISCOVERY_STARTING); + +unlock: + hci_dev_unlock(hdev); + return err; +} + +static int mgmt_stop_le_discovery_failed(struct hci_dev *hdev, u8 status) +{ + struct mgmt_pending_cmd *cmd; + int err; + + cmd = pending_find(MGMT_OP_STOP_LE_DISCOVERY, hdev); + if (!cmd) + return -ENOENT; + + err = mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode, + mgmt_status(status), &hdev->le_discovery.type, + sizeof(hdev->le_discovery.type)); + mgmt_pending_remove(cmd); + + return err; +} + +static void stop_le_discovery_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + BT_DBG("status %d", status); + + hci_dev_lock(hdev); + + if (status) { + mgmt_stop_le_discovery_failed(hdev, status); + goto unlock; + } + + hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED); + +unlock: + hci_dev_unlock(hdev); +} + +static int stop_le_discovery(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_stop_le_discovery *mgmt_cp = data; + struct mgmt_pending_cmd *cmd; + struct hci_request req; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (!hci_le_discovery_active(hdev)) { + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY, + MGMT_STATUS_REJECTED, &mgmt_cp->type, + sizeof(mgmt_cp->type)); + goto unlock; + } + + if (hdev->le_discovery.type != mgmt_cp->type) { + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS, + &mgmt_cp->type, sizeof(mgmt_cp->type)); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_STOP_LE_DISCOVERY, hdev, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_req_init(&req, hdev); + + if (hdev->le_discovery.state != DISCOVERY_FINDING) { + BT_DBG("unknown le discovery state %u", + hdev->le_discovery.state); + + mgmt_pending_remove(cmd); + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY, + MGMT_STATUS_FAILED, &mgmt_cp->type, + sizeof(mgmt_cp->type)); + goto unlock; + } + + cancel_delayed_work(&hdev->le_scan_disable); + hci_req_add_le_scan_disable(&req); + + err = hci_req_run(&req, stop_le_discovery_complete); + if (err < 0) + mgmt_pending_remove(cmd); + else + hci_le_discovery_set_state(hdev, DISCOVERY_STOPPING); + +unlock: + hci_dev_unlock(hdev); + return err; +} + +/* Separate LE discovery */ +void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering) +{ + struct mgmt_ev_discovering ev; + struct mgmt_pending_cmd *cmd; + + BT_DBG("%s le discovering %u", hdev->name, discovering); + + if (discovering) + cmd = pending_find(MGMT_OP_START_LE_DISCOVERY, hdev); + else + cmd = pending_find(MGMT_OP_STOP_LE_DISCOVERY, hdev); + + if (cmd) { + u8 type = hdev->le_discovery.type; + + mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type, + sizeof(type)); + mgmt_pending_remove(cmd); + } + + memset(&ev, 0, sizeof(ev)); + ev.type = hdev->le_discovery.type; + ev.discovering = discovering; + + mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -7723,6 +7990,8 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { set_enable_rssi, MGMT_SET_RSSI_ENABLE_SIZE }, { get_raw_rssi, MGMT_GET_RAW_RSSI_SIZE }, { set_disable_threshold, MGMT_SET_RSSI_DISABLE_SIZE }, + { start_le_discovery, MGMT_START_LE_DISCOVERY_SIZE }, + { stop_le_discovery, MGMT_STOP_LE_DISCOVERY_SIZE }, }; #endif -- 2.7.4 From 0c67df042ac4a0f96dff56e1c2fb7375ca971d30 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 11:41:34 +0530 Subject: [PATCH 06/16] Bluetooth: Add stop LE auto connection feature Added new MGMT command to disable LE auto connection. Change-Id: I1f5f61b83227501ad54019008b405fd47fd722a3 Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar --- include/net/bluetooth/mgmt_tizen.h | 5 +++++ net/bluetooth/mgmt.c | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 844af75..fab5beb 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -118,6 +118,11 @@ struct mgmt_cp_stop_le_discovery { #define MGMT_STOP_LE_DISCOVERY_SIZE 1 /* le discovery */ +/* For LE auto connection */ +#define MGMT_OP_DISABLE_LE_AUTO_CONNECT (TIZEN_OP_CODE_BASE + 0x0c) +#define MGMT_DISABLE_LE_AUTO_CONNECT_SIZE 0 +/* LE auto connection */ + /* EVENTS */ /* For device name update changes */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 21edaeb..8a60ca8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6119,6 +6119,24 @@ void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering) mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); } + +static int disable_le_auto_connect(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + err = hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); + if (err < 0) + BT_ERR("HCI_OP_LE_CREATE_CONN_CANCEL is failed"); + + hci_dev_unlock(hdev); + + return err; +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -7992,6 +8010,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { set_disable_threshold, MGMT_SET_RSSI_DISABLE_SIZE }, { 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 }, }; #endif -- 2.7.4 From fd00f774fb1bee0059912736dc4237b622e03727 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 11:58:22 +0530 Subject: [PATCH 07/16] 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 --- 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 3071066..212c439 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1643,6 +1643,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 u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, 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 25b89e7..0b179ea 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4681,12 +4681,26 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, 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 8a60ca8..671c9b0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6137,6 +6137,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) @@ -7108,6 +7176,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, u8 status, @@ -8011,6 +8108,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 From 0ff621e71a39bb506ee32b5e50cb42f6970b2439 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 12:13:09 +0530 Subject: [PATCH 08/16] Bluetooth: Set Manufacturer data feature Added new MGMT command to set the manufacturer data in the BR/EDR packet. Change-Id: Ie08062f4cad0c676deab94fd95fdc1a8c5602135 Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 6 ++ include/net/bluetooth/mgmt_tizen.h | 8 +++ net/bluetooth/mgmt.c | 122 +++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 212c439..7e36431 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -196,6 +196,10 @@ struct amp_assoc { #define HCI_MAX_PAGES 3 +#ifdef TIZEN_BT +#define HCI_MAX_EIR_MANUFACTURER_DATA_LENGTH 100 +#endif + struct hci_dev { struct list_head list; struct mutex lock; @@ -413,6 +417,8 @@ struct hci_dev { #ifdef TIZEN_BT __u8 adv_filter_policy; __u8 adv_type; + __u8 manufacturer_len; + __u8 manufacturer_data[HCI_MAX_EIR_MANUFACTURER_DATA_LENGTH]; #endif int (*open)(struct hci_dev *hdev); diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index c912a69..4f24c42 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -135,6 +135,14 @@ struct mgmt_cp_le_conn_update { } __packed; /* Add LE connection parameter update procedure */ +/* For Set Manufacturer Data */ +#define MGMT_OP_SET_MANUFACTURER_DATA (TIZEN_OP_CODE_BASE + 0x0e) +struct mgmt_cp_set_manufacturer_data { + __u8 data[100]; +} __packed; +#define MGMT_SET_MANUFACTURER_DATA_SIZE 100 +/* Set Manufacturer Data */ + /* EVENTS */ /* For device name update changes */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 671c9b0..90edd46 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6205,6 +6205,127 @@ static int le_conn_update(struct sock *sk, struct hci_dev *hdev, void *data, return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, 0, NULL, 0); } + +static void set_manufacturer_data_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_cp_set_manufacturer_data *cp; + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_SET_MANUFACTURER_DATA, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_SET_MANUFACTURER_DATA, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_SET_MANUFACTURER_DATA, 0, + cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int set_manufacturer_data(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct hci_request req; + struct mgmt_cp_set_manufacturer_data *cp = data; + u8 old_data[HCI_MAX_EIR_LENGTH] = {0, }; + u8 old_len; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_bredr_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_MANUFACTURER_DATA, + MGMT_STATUS_NOT_SUPPORTED); + + if (cp->data[0] == 0 || + cp->data[0] - 1 > sizeof(hdev->manufacturer_data)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_MANUFACTURER_DATA, + MGMT_STATUS_INVALID_PARAMS); + + if (cp->data[1] != 0xFF) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_MANUFACTURER_DATA, + MGMT_STATUS_NOT_SUPPORTED); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_SET_MANUFACTURER_DATA, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_MANUFACTURER_DATA, + MGMT_STATUS_BUSY); + goto unlocked; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_MANUFACTURER_DATA, hdev, data, + len); + if (!cmd) { + err = -ENOMEM; + goto unlocked; + } + + hci_req_init(&req, hdev); + + /* if new data is same as previous data then return command + * complete event + */ + if (hdev->manufacturer_len == cp->data[0] - 1 && + !memcmp(hdev->manufacturer_data, cp->data + 2, cp->data[0] - 1)) { + mgmt_pending_remove(cmd); + mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_MANUFACTURER_DATA, + 0, cp, sizeof(*cp)); + err = 0; + goto unlocked; + } + + old_len = hdev->manufacturer_len; + if (old_len > 0) + memcpy(old_data, hdev->manufacturer_data, old_len); + + hdev->manufacturer_len = cp->data[0] - 1; + if (hdev->manufacturer_len > 0) + memcpy(hdev->manufacturer_data, cp->data + 2, + hdev->manufacturer_len); + + __hci_req_update_eir(&req); + + err = hci_req_run(&req, set_manufacturer_data_complete); + if (err < 0) { + mgmt_pending_remove(cmd); + goto failed; + } + +unlocked: + hci_dev_unlock(hdev); + + return err; + +failed: + memset(hdev->manufacturer_data, 0x00, sizeof(hdev->manufacturer_data)); + hdev->manufacturer_len = old_len; + if (hdev->manufacturer_len > 0) + memcpy(hdev->manufacturer_data, old_data, + hdev->manufacturer_len); + hci_dev_unlock(hdev); + return err; +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -8109,6 +8230,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { 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 }, + { set_manufacturer_data, MGMT_SET_MANUFACTURER_DATA_SIZE }, }; #endif -- 2.7.4 From 4aff4932c11a82a44bc9e8ef19c120a1b424fd38 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 25 Aug 2016 12:46:07 +0530 Subject: [PATCH 09/16] Bluetooth: Add set LE scan parameter feature Added new MGMT command to set LE scan parameters Change-Id: I5ea660f97e93dfcc72273971ad0250e7f582f718 Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar --- include/net/bluetooth/mgmt_tizen.h | 8 ++++++ net/bluetooth/mgmt.c | 59 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 4f24c42..4c2e9d0 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -143,6 +143,14 @@ struct mgmt_cp_set_manufacturer_data { #define MGMT_SET_MANUFACTURER_DATA_SIZE 100 /* Set Manufacturer Data */ +#define MGMT_OP_LE_SET_SCAN_PARAMS (TIZEN_OP_CODE_BASE + 0x0f) +struct mgmt_cp_le_set_scan_params { + __u8 type; /* le scan type */ + __le16 interval; + __le16 window; +} __packed; +#define MGMT_LE_SET_SCAN_PARAMS_SIZE 5 + /* EVENTS */ /* For device name update changes */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 90edd46..3023f9a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6326,6 +6326,64 @@ failed: hci_dev_unlock(hdev); return err; } + +static int le_set_scan_params(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_le_set_scan_params *cp = data; + __u16 interval, window; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS, + MGMT_STATUS_NOT_SUPPORTED); + + interval = __le16_to_cpu(cp->interval); + + if (interval < 0x0004 || interval > 0x4000) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS, + MGMT_STATUS_INVALID_PARAMS); + + window = __le16_to_cpu(cp->window); + + if (window < 0x0004 || window > 0x4000) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS, + MGMT_STATUS_INVALID_PARAMS); + + if (window > interval) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + hdev->le_scan_type = cp->type; + hdev->le_scan_interval = interval; + hdev->le_scan_window = window; + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS, 0, + NULL, 0); + + /* If background scan is running, restart it so new parameters are + * loaded. + */ + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && + hdev->discovery.state == DISCOVERY_STOPPED) { + struct hci_request req; + + hci_req_init(&req, hdev); + + hci_req_add_le_scan_disable(&req); + hci_req_add_le_passive_scan(&req); + + hci_req_run(&req, NULL); + } + + hci_dev_unlock(hdev); + + return err; +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -8231,6 +8289,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { disable_le_auto_connect, MGMT_DISABLE_LE_AUTO_CONNECT_SIZE }, { le_conn_update, MGMT_LE_CONN_UPDATE_SIZE }, { set_manufacturer_data, MGMT_SET_MANUFACTURER_DATA_SIZE }, + { le_set_scan_params, MGMT_LE_SET_SCAN_PARAMS_SIZE }, }; #endif -- 2.7.4 From 4d1e4223f4c5ad4b87f9bb2ec19bdbe25d2a4249 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 8 Sep 2016 10:56:15 +0530 Subject: [PATCH 10/16] Bluetooth: Add LE vendor specific event handler This patch adds the vendor specific LE meta event handler. It handles the vendor specific handles like, LE_MULTI_ADV_STATE_CHANGE_SUB_EVENT, LE_RSSI_LINK_ALERT. Change-Id: I1f344a31e36f9c7442fe0bd8b598e67d9f5fb9bf Signed-off-by: Sudha Bheemanna [divide hci vendor speicif group event function] Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci.h | 19 +++++++++++++++++ net/bluetooth/hci_event.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5ce0292..d45f7ee 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1920,6 +1920,25 @@ struct hci_ev_sync_train_complete { #define HCI_EV_SLAVE_PAGE_RESP_TIMEOUT 0x54 +#ifdef TIZEN_BT +/* + * Vendor Specific HCI Event + * Vendor: Broadcom + * Purpose: This HCI Event gives RSSI Alerts for monitored LE Link + */ +#define HCI_EV_VENDOR_SPECIFIC 0xFF +struct hci_ev_vendor_specific { + __u8 event_sub_code; +} __packed; + +#define LE_META_VENDOR_SPECIFIC_GROUP_EVENT 0xE9 +struct hci_ev_ext_vendor_specific { + __u8 event_le_ext_sub_code; +} __packed; + +#define LE_RSSI_LINK_ALERT 0x02 +#endif + #define HCI_EV_LE_CONN_COMPLETE 0x01 struct hci_ev_le_conn_complete { __u8 status; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0b179ea..e8ef30e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1418,6 +1418,50 @@ static void hci_cc_get_raw_rssi(struct hci_dev *hdev, mgmt_raw_rssi_response(hdev, rp, rp->status); } + +static void hci_vendor_specific_group_ext_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_ext_vendor_specific *ev = (void *)skb->data; + __u8 event_le_ext_sub_code; + + BT_DBG("RSSI event LE_META_VENDOR_SPECIFIC_GROUP_EVENT: %X", + LE_META_VENDOR_SPECIFIC_GROUP_EVENT); + + skb_pull(skb, sizeof(*ev)); + event_le_ext_sub_code = ev->event_le_ext_sub_code; + + switch (event_le_ext_sub_code) { + case LE_RSSI_LINK_ALERT: + BT_DBG("RSSI event LE_RSSI_LINK_ALERT %X", + LE_RSSI_LINK_ALERT); + mgmt_rssi_alert_evt(hdev, skb); + break; + + default: + break; + } +} + +static void hci_vendor_specific_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_vendor_specific *ev = (void *)skb->data; + __u8 event_sub_code; + + BT_DBG("hci_vendor_specific_evt"); + + skb_pull(skb, sizeof(*ev)); + event_sub_code = ev->event_sub_code; + + switch (event_sub_code) { + case LE_META_VENDOR_SPECIFIC_GROUP_EVENT: + hci_vendor_specific_group_ext_evt(hdev, skb); + break; + + default: + break; + } +} #endif static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb) @@ -5519,6 +5563,12 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_num_comp_blocks_evt(hdev, skb); break; +#ifdef TIZEN_BT + case HCI_EV_VENDOR_SPECIFIC: + hci_vendor_specific_evt(hdev, skb); + break; +#endif + default: BT_DBG("%s event 0x%2.2x", hdev->name, event); break; -- 2.7.4 From 68a00598e885e9db4c339fee50c9f29a0d694b36 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Wed, 7 Sep 2016 15:42:22 +0530 Subject: [PATCH 11/16] Bluetooth: Add hardware error MGMT event Add code to handle hardware error MGMT event. Change-Id: I5875ea2aeae7aba95c1f8e15b456704dc6bcdee4 Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt_tizen.h | 7 +++++++ net/bluetooth/hci_event.c | 5 +++++ net/bluetooth/mgmt.c | 8 ++++++++ 4 files changed, 21 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7e36431..3d9278e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1654,6 +1654,7 @@ int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, u16 supervision_timeout); int mgmt_le_conn_update_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); +void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code); #endif u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 4c2e9d0..680ad8b 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -162,6 +162,13 @@ struct mgmt_ev_device_name_update { } __packed; /* Device name update changes */ +/* For handling of hardware error event */ +#define MGMT_EV_HARDWARE_ERROR (TIZEN_EV_BASE + 0x02) +struct mgmt_ev_hardware_error { + __u8 error_code; +} __packed; +/* handling of hardware error event */ + /* For handling of RSSI Events */ #define MGMT_EV_RSSI_ALERT (TIZEN_EV_BASE + 0x04) struct mgmt_ev_vendor_specific_rssi_alert { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e8ef30e..63890dc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3246,6 +3246,11 @@ static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_hardware_error *ev = (void *) skb->data; +#ifdef TIZEN_BT + hci_dev_lock(hdev); + mgmt_hardware_error(hdev, ev->code); + hci_dev_unlock(hdev); +#endif hdev->hw_error_code = ev->code; queue_work(hdev->req_workqueue, &hdev->error_reset); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3023f9a..0d6829a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6384,6 +6384,14 @@ static int le_set_scan_params(struct sock *sk, struct hci_dev *hdev, return err; } + +void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code) +{ + struct mgmt_ev_hardware_error ev; + + ev.error_code = err_code; + mgmt_event(MGMT_EV_HARDWARE_ERROR, hdev, &ev, sizeof(ev), NULL); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) -- 2.7.4 From 4510f6ad92d59933ecfd2f83d65a7b48708266fc Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Wed, 7 Sep 2016 16:47:58 +0530 Subject: [PATCH 12/16] Bluetooth: Add H/W TX timeout error MGMT event This patch sends the H/W TX timeout error MGMT event if HCI command timeout occurs after sending HCI commands. Change-Id: I5eb593f2fe4d31c404dd94ef582790e47d03b10a Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar --- include/net/bluetooth/hci.h | 4 ++++ include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt_tizen.h | 4 ++++ net/bluetooth/hci_core.c | 11 +++++++++++ net/bluetooth/mgmt.c | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d45f7ee..9f3948e 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -268,7 +268,11 @@ enum { #define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */ #define HCI_INIT_TIMEOUT msecs_to_jiffies(10000) /* 10 seconds */ +#ifdef TIZEN_BT +#define HCI_CMD_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */ +#else #define HCI_CMD_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ +#endif #define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */ #define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3d9278e..b38f811 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1655,6 +1655,7 @@ int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, int mgmt_le_conn_update_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code); +void mgmt_tx_timeout_error(struct hci_dev *hdev); #endif u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 680ad8b..fd5a9fe 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -169,6 +169,10 @@ struct mgmt_ev_hardware_error { } __packed; /* handling of hardware error event */ +/* For HCI TX Timeout Error */ +#define MGMT_EV_TX_TIMEOUT_ERROR (TIZEN_EV_BASE + 0x03) +/* HCI TX Timeout Error */ + /* For handling of RSSI Events */ #define MGMT_EV_RSSI_ALERT (TIZEN_EV_BASE + 0x04) struct mgmt_ev_vendor_specific_rssi_alert { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ae9d874..301b1e0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1036,6 +1036,13 @@ void hci_le_discovery_set_state(struct hci_dev *hdev, int state) hdev->le_discovery.state = state; } + +static void hci_tx_timeout_error_evt(struct hci_dev *hdev) +{ + BT_ERR("%s H/W TX Timeout error", hdev->name); + + mgmt_tx_timeout_error(hdev); +} #endif void hci_inquiry_cache_flush(struct hci_dev *hdev) @@ -2538,6 +2545,10 @@ static void hci_cmd_timeout(struct work_struct *work) BT_ERR("%s command tx timeout", hdev->name); } +#ifdef TIZEN_BT + hci_tx_timeout_error_evt(hdev); +#endif + atomic_set(&hdev->cmd_cnt, 1); queue_work(hdev->workqueue, &hdev->cmd_work); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0d6829a..aaa3bf2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6392,6 +6392,11 @@ void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code) ev.error_code = err_code; mgmt_event(MGMT_EV_HARDWARE_ERROR, hdev, &ev, sizeof(ev), NULL); } + +void mgmt_tx_timeout_error(struct hci_dev *hdev) +{ + mgmt_event(MGMT_EV_TX_TIMEOUT_ERROR, hdev, NULL, 0, NULL); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) -- 2.7.4 From 4546c992ef84c989d31bc49e84253a5d4f7d91db Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Thu, 29 Sep 2016 01:59:05 +0900 Subject: [PATCH 13/16] Bluetooth: fix vendor ext rssi link alert event This patch fixes style for rssi link alert event from vendor specific group ext. Change-Id: I0e7003e417c5f5a590cce8264caccad515dd3c10 Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci.h | 11 +++++------ include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_event.c | 15 ++++++++++++--- net/bluetooth/mgmt.c | 12 ++++++------ 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 9f3948e..9af1803 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1532,12 +1532,6 @@ struct hci_cc_rsp_enable_rssi { __u8 le_ext_opcode; } __packed; -struct hci_ev_vendor_specific_rssi_alert { - __le16 conn_handle; - __s8 alert_type; - __s8 rssi_dbm; -} __packed; - /* * Vendor Specific HCI Command * Vendor: Broadcom @@ -1941,6 +1935,11 @@ struct hci_ev_ext_vendor_specific { } __packed; #define LE_RSSI_LINK_ALERT 0x02 +struct hci_ev_vendor_specific_rssi_alert { + __le16 conn_handle; + __s8 alert_type; + __s8 rssi_dbm; +} __packed; #endif #define HCI_EV_LE_CONN_COMPLETE 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b38f811..b503f05 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1642,7 +1642,8 @@ void mgmt_rssi_disable_success(struct sock *sk, struct hci_dev *hdev, void *data, struct hci_cc_rsp_enable_rssi *rp, int success); int mgmt_set_rssi_threshold(struct sock *sk, struct hci_dev *hdev, void *data, u16 len); -void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb); +void mgmt_rssi_alert_evt(struct hci_dev *hdev, u16 conn_handle, + s8 alert_type, s8 rssi_dbm); void mgmt_raw_rssi_response(struct hci_dev *hdev, struct hci_cc_rp_get_raw_rssi *rp, int success); void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 63890dc..8f12f09 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1419,6 +1419,17 @@ static void hci_cc_get_raw_rssi(struct hci_dev *hdev, mgmt_raw_rssi_response(hdev, rp, rp->status); } +static void hci_vendor_ext_rssi_link_alert_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_vendor_specific_rssi_alert *ev = (void *)skb->data; + + BT_DBG("RSSI event LE_RSSI_LINK_ALERT %X", LE_RSSI_LINK_ALERT); + + mgmt_rssi_alert_evt(hdev, ev->conn_handle, ev->alert_type, + ev->rssi_dbm); +} + static void hci_vendor_specific_group_ext_evt(struct hci_dev *hdev, struct sk_buff *skb) { @@ -1433,9 +1444,7 @@ static void hci_vendor_specific_group_ext_evt(struct hci_dev *hdev, switch (event_le_ext_sub_code) { case LE_RSSI_LINK_ALERT: - BT_DBG("RSSI event LE_RSSI_LINK_ALERT %X", - LE_RSSI_LINK_ALERT); - mgmt_rssi_alert_evt(hdev, skb); + hci_vendor_ext_rssi_link_alert_evt(hdev, skb); break; default: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index aaa3bf2..b0e2ee4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5823,16 +5823,16 @@ unlocked: return err; } -void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb) +void mgmt_rssi_alert_evt(struct hci_dev *hdev, u16 conn_handle, + s8 alert_type, s8 rssi_dbm) { - struct hci_ev_vendor_specific_rssi_alert *ev = (void *)skb->data; struct mgmt_ev_vendor_specific_rssi_alert mgmt_ev; struct hci_conn *conn; BT_DBG("RSSI alert [%2.2X %2.2X %2.2X]", - ev->conn_handle, ev->alert_type, ev->rssi_dbm); + conn_handle, alert_type, rssi_dbm); - conn = hci_conn_hash_lookup_handle(hdev, ev->conn_handle); + conn = hci_conn_hash_lookup_handle(hdev, conn_handle); if (!conn) { BT_ERR("RSSI alert Error: Device not found for handle"); @@ -5845,8 +5845,8 @@ void mgmt_rssi_alert_evt(struct hci_dev *hdev, struct sk_buff *skb) else mgmt_ev.link_type = 0x00; - mgmt_ev.alert_type = ev->alert_type; - mgmt_ev.rssi_dbm = ev->rssi_dbm; + mgmt_ev.alert_type = alert_type; + mgmt_ev.rssi_dbm = rssi_dbm; mgmt_event(MGMT_EV_RSSI_ALERT, hdev, &mgmt_ev, sizeof(struct mgmt_ev_vendor_specific_rssi_alert), -- 2.7.4 From 36dd935a9149ed0dd4705adc1692f18c03bab74b Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 8 Sep 2016 10:10:03 +0530 Subject: [PATCH 14/16] Bluetooth: Add LE device found MGMT event This patch adds new MGMT event for LE device discovery and allows the handling of all advertisement packets in platform. Change-Id: I1927acb75eff0b60a5899898c6d7a000e1a108ef Signed-off-by: Sudha Bheemanna Signed-off-by: Amit Purwar --- include/net/bluetooth/hci.h | 7 +++++++ include/net/bluetooth/hci_core.h | 6 ++++++ include/net/bluetooth/mgmt_tizen.h | 22 ++++++++++++++++++++ net/bluetooth/hci_event.c | 20 ++++++++++++++++++ net/bluetooth/mgmt.c | 42 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 9af1803..7e14055 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1940,6 +1940,13 @@ struct hci_ev_vendor_specific_rssi_alert { __s8 alert_type; __s8 rssi_dbm; } __packed; + +#define LE_MULTI_ADV_STATE_CHANGE_SUB_EVENT 0x55 +struct hci_ev_vendor_specific_multi_adv_state { + __u8 adv_instance; + __u8 state_change_reason; + __le16 connection_handle; +} __packed; #endif #define HCI_EV_LE_CONN_COMPLETE 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b503f05..b645066 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1657,6 +1657,12 @@ int mgmt_le_conn_update_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code); void mgmt_tx_timeout_error(struct hci_dev *hdev); +/* Pass adv type in the le device found */ +void mgmt_le_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, u8 *eir, + u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len, u8 adv_type); +void mgmt_multi_adv_state_change_evt(struct hci_dev *hdev, u8 adv_instance, + u8 state_change_reason, u16 connection_handle); #endif u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index fd5a9fe..504c2d4 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -211,4 +211,26 @@ struct mgmt_ev_conn_update_failed { } __packed; /* Add LE connection update Events */ +/* For LE device found event */ +#define MGMT_EV_LE_DEVICE_FOUND (TIZEN_EV_BASE + 0x0a) +struct mgmt_ev_le_device_found { + struct mgmt_addr_info addr; + __s8 rssi; + __le32 flags; + __s8 adv_type; + __le16 eir_len; + __u8 eir[0]; +} __packed; +/* LE device found event */ + +/* For LE advertisement state changed event */ +#define MGMT_EV_MULTI_ADV_STATE_CHANGED (TIZEN_EV_BASE + 0x0b) +struct mgmt_ev_vendor_specific_multi_adv_state_changed { + __u8 adv_instance; + __u8 state_change_reason; + __le16 connection_handle; +} __packed; +/* LE advertisement state changed event */ + + #endif /* __MGMT_TIZEN_H */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8f12f09..53c5285 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1127,6 +1127,7 @@ static void clear_pending_adv_report(struct hci_dev *hdev) d->last_adv_data_len = 0; } +#ifndef TIZEN_BT static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type, s8 rssi, u32 flags, u8 *data, u8 len) @@ -1143,6 +1144,7 @@ static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr, memcpy(d->last_adv_data, data, len); d->last_adv_data_len = len; } +#endif static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) @@ -4854,10 +4856,14 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, u8 bdaddr_type, bdaddr_t *direct_addr, u8 direct_addr_type, s8 rssi, u8 *data, u8 len) { +#ifndef TIZEN_BT struct discovery_state *d = &hdev->discovery; +#endif struct smp_irk *irk; struct hci_conn *conn; +#ifndef TIZEN_BT bool match; +#endif u32 flags; u8 *ptr, real_len; @@ -4955,16 +4961,24 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, if (type == LE_ADV_DIRECT_IND) return; +#ifndef TIZEN_BT + /* Handle all adv packet in platform */ if (!hci_pend_le_action_lookup(&hdev->pend_le_reports, bdaddr, bdaddr_type)) return; +#endif if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND) flags = MGMT_DEV_FOUND_NOT_CONNECTABLE; else flags = 0; +#ifdef TIZEN_BT + mgmt_le_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, + rssi, flags, data, len, NULL, 0, type); +#else mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, rssi, flags, data, len, NULL, 0); +#endif return; } @@ -4989,6 +5003,11 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, else flags = 0; +#ifdef TIZEN_BT + /* Disable adv ind and scan rsp merging */ + mgmt_le_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, + rssi, flags, data, len, NULL, 0, type); +#else /* If there's nothing pending either store the data from this * event or send an immediate device found event if the data * should not be stored for later. @@ -5051,6 +5070,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, d->last_adv_addr_type, NULL, rssi, d->last_adv_flags, d->last_adv_data, d->last_adv_data_len, data, len); clear_pending_adv_report(hdev); +#endif } static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b0e2ee4..1cc3663 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7397,6 +7397,48 @@ int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr, return mgmt_event(MGMT_EV_CONN_UPDATED, hdev, &ev, sizeof(ev), NULL); } + +/* le device found event - Pass adv type */ +void mgmt_le_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, u8 *eir, + u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len, u8 adv_type) +{ + char buf[512]; + struct mgmt_ev_le_device_found *ev = (void *)buf; + size_t ev_size; + + if (!hci_discovery_active(hdev) && !hci_le_discovery_active(hdev)) + return; + + /* Make sure that the buffer is big enough. The 5 extra bytes + * are for the potential CoD field. + */ + if (sizeof(*ev) + eir_len + scan_rsp_len + 5 > sizeof(buf)) + return; + + memset(buf, 0, sizeof(buf)); + + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = link_to_bdaddr(link_type, addr_type); + ev->rssi = rssi; + ev->flags = cpu_to_le32(flags); + ev->adv_type = adv_type; + + if (eir_len > 0) + memcpy(ev->eir, eir, eir_len); + + if (dev_class && !eir_get_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, NULL)) + eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, + dev_class, 3); + + if (scan_rsp_len > 0) + memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len); + + ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len); + ev_size = sizeof(*ev) + eir_len + scan_rsp_len; + + mgmt_event(MGMT_EV_LE_DEVICE_FOUND, hdev, ev, ev_size, NULL); +} #endif static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status, -- 2.7.4 From 15d3a04ebf39f5bd6b4b0560ab502ffb1124d5b2 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Thu, 8 Sep 2016 10:31:17 +0530 Subject: [PATCH 15/16] Bluetooth: Add multiple LE advertise state change event This patch adds code for providing multiple LE advertisement state changed event to upper layer. Change-Id: I58fb8044e74402376ec30b121081edce7c8709d1 Signed-off-by: Sudha Bheemanna [divide hci_vendor_mutli_adv_state_change_evt and remove hci event structure from mgmt] Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- net/bluetooth/hci_event.c | 16 ++++++++++++++++ net/bluetooth/mgmt.c | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 53c5285..17a734c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1454,6 +1454,18 @@ static void hci_vendor_specific_group_ext_evt(struct hci_dev *hdev, } } +static void hci_vendor_multi_adv_state_change_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_vendor_specific_multi_adv_state *ev = (void *)skb->data; + + BT_DBG("LE_MULTI_ADV_STATE_CHANGE_SUB_EVENT"); + + mgmt_multi_adv_state_change_evt(hdev, ev->adv_instance, + ev->state_change_reason, + ev->connection_handle); +} + static void hci_vendor_specific_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_vendor_specific *ev = (void *)skb->data; @@ -1469,6 +1481,10 @@ static void hci_vendor_specific_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_vendor_specific_group_ext_evt(hdev, skb); break; + case LE_MULTI_ADV_STATE_CHANGE_SUB_EVENT: + hci_vendor_multi_adv_state_change_evt(hdev, skb); + break; + default: break; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1cc3663..8b498f2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6397,6 +6397,23 @@ void mgmt_tx_timeout_error(struct hci_dev *hdev) { mgmt_event(MGMT_EV_TX_TIMEOUT_ERROR, hdev, NULL, 0, NULL); } + +void mgmt_multi_adv_state_change_evt(struct hci_dev *hdev, u8 adv_instance, + u8 state_change_reason, u16 connection_handle) +{ + struct mgmt_ev_vendor_specific_multi_adv_state_changed mgmt_ev; + + BT_DBG("Multi adv state changed [%2.2X %2.2X %2.2X]", + adv_instance, state_change_reason, connection_handle); + + mgmt_ev.adv_instance = adv_instance; + mgmt_ev.state_change_reason = state_change_reason; + mgmt_ev.connection_handle = connection_handle; + + mgmt_event(MGMT_EV_MULTI_ADV_STATE_CHANGED, hdev, &mgmt_ev, + sizeof(struct mgmt_ev_vendor_specific_multi_adv_state_changed), + NULL); +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) -- 2.7.4 From 2bcbbea6106ff9e804c0053fa8d715d039906024 Mon Sep 17 00:00:00 2001 From: Sudha Bheemanna Date: Tue, 6 Sep 2016 16:38:36 +0530 Subject: [PATCH 16/16] Bluetooth: Add MGMT command to set SCO settings Added code to set sco settings. Change-Id: I37aa572436241b06e00d1e9e75964aac747eeba5 Signed-off-by: Sudha Bheemanna [remove sco link policy part] Signed-off-by: Seung-Woo Kim Signed-off-by: Amit Purwar --- include/net/bluetooth/hci.h | 23 +++++ include/net/bluetooth/hci_core.h | 19 ++++ include/net/bluetooth/mgmt_tizen.h | 10 ++ include/net/bluetooth/sco.h | 7 ++ net/bluetooth/hci_conn.c | 19 ++++ net/bluetooth/mgmt.c | 57 ++++++++++++ net/bluetooth/sco.c | 182 +++++++++++++++++++++++++++++++++++++ 7 files changed, 317 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7e14055..3fdc0c0 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1548,6 +1548,29 @@ struct hci_cc_rp_get_raw_rssi { __le16 conn_handle; __s8 rssi_dbm; } __packed; + +#define HCI_BCM_ENABLE_WBS_REQ 0xfc7e +struct hci_cp_bcm_wbs_req { + __u8 role; + __le16 pkt_type; +} __packed; + +#define HCI_BCM_I2C_PCM_REQ 0xfc6d +struct hci_cp_i2c_pcm_req { + __u8 i2c_enable; + __u8 is_master; + __u8 pcm_rate; + __u8 clock_rate; +} __packed; + +#define HCI_BCM_SCO_PCM_REQ 0xfc1c +struct hci_cp_sco_pcm_req { + __u8 sco_routing; + __u8 pcm_rate; + __u8 frame_type; + __u8 sync_mode; + __u8 clock_mode; +} __packed; #endif /* ---- HCI Events ---- */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b645066..3ddbdbc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -514,6 +514,8 @@ struct hci_conn { #ifdef TIZEN_BT bool rssi_monitored; + __u8 sco_role; + __u16 voice_setting; #endif struct hci_conn *link; @@ -958,6 +960,23 @@ static inline int hci_conn_hash_lookup_rssi_count(struct hci_dev *hdev) bool hci_le_discovery_active(struct hci_dev *hdev); void hci_le_discovery_set_state(struct hci_dev *hdev, int state); + +static inline struct hci_conn *hci_conn_hash_lookup_sco(struct hci_dev *hdev) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *c; + + rcu_read_lock(); + list_for_each_entry_rcu(c, &h->list, list) { + if (c->type == SCO_LINK || c->type == ESCO_LINK) { + rcu_read_unlock(); + return c; + } + } + rcu_read_unlock(); + + return NULL; +} #endif int hci_disconnect(struct hci_conn *conn, __u8 reason); diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index 504c2d4..4eaa867 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -151,6 +151,16 @@ struct mgmt_cp_le_set_scan_params { } __packed; #define MGMT_LE_SET_SCAN_PARAMS_SIZE 5 +#define MGMT_SCO_ROLE_HANDSFREE 0x00 +#define MGMT_SCO_ROLE_AUDIO_GATEWAY 0x01 +#define MGMT_OP_SET_VOICE_SETTING (TIZEN_OP_CODE_BASE + 0x10) +struct mgmt_cp_set_voice_setting { + bdaddr_t bdaddr; + uint8_t sco_role; + uint16_t voice_setting; +} __packed; +#define MGMT_SET_VOICE_SETTING_SIZE 9 + /* EVENTS */ /* For device name update changes */ diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index f40ddb4..bca05bf 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -46,4 +46,11 @@ struct sco_conninfo { __u8 dev_class[3]; }; +#ifdef TIZEN_BT +void sco_connect_set_gw_nbc(struct hci_dev *hdev); +void sco_connect_set_gw_wbc(struct hci_dev *hdev); +void sco_connect_set_nbc(struct hci_dev *hdev); +void sco_connect_set_wbc(struct hci_dev *hdev); +#endif + #endif /* __SCO_H */ diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e3cd81c..074c8f1 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -41,6 +41,16 @@ struct sco_param { u8 retrans_effort; }; +#ifdef TIZEN_BT +static const struct sco_param esco_param_cvsd[] = { + { (EDR_ESCO_MASK & ~ESCO_2EV3) | SCO_ESCO_MASK | ESCO_EV3, + 0x000a, 0x01 }, /* S3 */ + { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007, 0x01 }, /* S2 */ + { EDR_ESCO_MASK | ESCO_EV3, 0x0007, 0x01 }, /* S1 */ + { EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0x01 }, /* D1 */ + { EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0x01 }, /* D0 */ +}; +#else static const struct sco_param esco_param_cvsd[] = { { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a, 0x01 }, /* S3 */ { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007, 0x01 }, /* S2 */ @@ -48,16 +58,25 @@ static const struct sco_param esco_param_cvsd[] = { { EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0x01 }, /* D1 */ { EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0x01 }, /* D0 */ }; +#endif static const struct sco_param sco_param_cvsd[] = { { EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0xff }, /* D1 */ { EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0xff }, /* D0 */ }; +#ifdef TIZEN_BT +static const struct sco_param esco_param_msbc[] = { + { (EDR_ESCO_MASK & ~ESCO_2EV3) | ESCO_EV3, + 0x000d, 0x02 }, /* T2 */ + { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d, 0x02 }, /* T2 */ +}; +#else static const struct sco_param esco_param_msbc[] = { { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d, 0x02 }, /* T2 */ { EDR_ESCO_MASK | ESCO_EV3, 0x0008, 0x02 }, /* T1 */ }; +#endif /* This function requires the caller holds hdev->lock */ static void hci_connect_le_scan_cleanup(struct hci_conn *conn) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8b498f2..0c8bec1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -34,6 +34,7 @@ #include #ifdef TIZEN_BT #include +#include #endif #include "hci_request.h" @@ -6385,6 +6386,61 @@ static int le_set_scan_params(struct sock *sk, struct hci_dev *hdev, return err; } +static int set_voice_setting(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_voice_setting *cp = data; + struct hci_conn *conn; + struct hci_conn *sco_conn; + + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_bredr_capable(hdev)) { + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_VOICE_SETTING, + MGMT_STATUS_NOT_SUPPORTED); + } + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (!conn) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_SET_VOICE_SETTING, 0, NULL, 0); + goto unlock; + } + + conn->voice_setting = cp->voice_setting; + conn->sco_role = cp->sco_role; + + sco_conn = hci_conn_hash_lookup_sco(hdev); + if (sco_conn && bacmp(&sco_conn->dst, &cp->bdaddr) != 0) { + BT_ERR("There is other SCO connection."); + goto done; + } + + if (conn->sco_role == MGMT_SCO_ROLE_HANDSFREE) { + if (conn->voice_setting == 0x0063) + sco_connect_set_wbc(hdev); + else + sco_connect_set_nbc(hdev); + } else { + if (conn->voice_setting == 0x0063) + sco_connect_set_gw_wbc(hdev); + else + sco_connect_set_gw_nbc(hdev); + } + +done: + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_VOICE_SETTING, 0, + cp, sizeof(cp)); + +unlock: + hci_dev_unlock(hdev); + return err; +} + void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code) { struct mgmt_ev_hardware_error ev; @@ -8362,6 +8418,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { le_conn_update, MGMT_LE_CONN_UPDATE_SIZE }, { set_manufacturer_data, MGMT_SET_MANUFACTURER_DATA_SIZE }, { le_set_scan_params, MGMT_LE_SET_SCAN_PARAMS_SIZE }, + { set_voice_setting, MGMT_SET_VOICE_SETTING_SIZE }, }; #endif diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 95fd7a8..c665a5e 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -32,6 +32,11 @@ #include #include +#ifdef TIZEN_BT +#include +#include +#endif + static bool disable_esco; static const struct proto_ops sco_sock_ops; @@ -839,8 +844,15 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, } /* Explicitly check for these values */ +#ifdef TIZEN_BT + /* Codec defer accept */ + if (voice.setting != (BT_VOICE_TRANSPARENT | + BT_VOICE_CVSD_16BIT) && + voice.setting != BT_VOICE_CVSD_16BIT) { +#else if (voice.setting != BT_VOICE_TRANSPARENT && voice.setting != BT_VOICE_CVSD_16BIT) { +#endif err = -EINVAL; break; } @@ -1016,6 +1028,10 @@ static int sco_sock_release(struct socket *sock) release_sock(sk); } +#ifdef TIZEN_BT + /* SCO kernel panic fix */ + bt_sock_unlink(&sco_sk_list, sk); +#endif sock_orphan(sk); sco_sock_kill(sk); return err; @@ -1042,7 +1058,12 @@ static void sco_conn_ready(struct sco_conn *conn) return; } +#ifdef TIZEN_BT + /* Multiple SCO accept feature */ + parent = sco_get_sock_listen(&conn->hcon->dst); +#else parent = sco_get_sock_listen(&conn->hcon->src); +#endif if (!parent) { sco_conn_unlock(conn); return; @@ -1080,6 +1101,131 @@ static void sco_conn_ready(struct sco_conn *conn) } } +#ifdef TIZEN_BT +/* WBC/NBC feature */ +void sco_connect_set_gw_nbc(struct hci_dev *hdev) +{ + struct hci_cp_write_voice_setting cp1; + struct hci_cp_bcm_wbs_req cp2; + struct hci_cp_i2c_pcm_req cp3; + struct hci_cp_sco_pcm_req cp4; + + BT_DBG("Setting the NBC params, hdev = %p", hdev); + + cp1.voice_setting = cpu_to_le16(0x0060); + hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1); + hdev->voice_setting = cpu_to_le16(0x0060); + + cp2.role = 0x00; /* WbDisable */ + cp2.pkt_type = cpu_to_le16(0x0002); + hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2); + + cp3.i2c_enable = 0x01; + cp3.is_master = 0x00; + cp3.pcm_rate = 0x00; + cp3.clock_rate = 0x01; + hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3); + + cp4.sco_routing = 0x00; + cp4.pcm_rate = 0x01; + cp4.frame_type = 0x00; + cp4.sync_mode = 0x00; + cp4.clock_mode = 0x00; + hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4); +} + +void sco_connect_set_gw_wbc(struct hci_dev *hdev) +{ + struct hci_cp_write_voice_setting cp1; + struct hci_cp_bcm_wbs_req cp2; + struct hci_cp_i2c_pcm_req cp3; + struct hci_cp_sco_pcm_req cp4; + + BT_DBG("Setting the WBC params, hdev = %p", hdev); + cp1.voice_setting = cpu_to_le16(0x0003 | 0x0060); + hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1); + hdev->voice_setting = cpu_to_le16(0x0003 | 0x0060); + + cp2.role = 0x01; /* Enable */ + cp2.pkt_type = cpu_to_le16(0x0002); + hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2); + + cp3.i2c_enable = 0x00; + cp3.is_master = 0x00; + cp3.pcm_rate = 0x01; + cp3.clock_rate = 0x02; + hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3); + + cp4.sco_routing = 0x00; + cp4.pcm_rate = 0x00; + cp4.frame_type = 0x00; + cp4.sync_mode = 0x00; + cp4.clock_mode = 0x00; + hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4); +} + +void sco_connect_set_nbc(struct hci_dev *hdev) +{ + struct hci_cp_write_voice_setting cp1; + struct hci_cp_bcm_wbs_req cp2; + struct hci_cp_i2c_pcm_req cp3; + struct hci_cp_sco_pcm_req cp4; + + BT_DBG("Setting the NBC params, hdev = %p", hdev); + + cp1.voice_setting = cpu_to_le16(0x0060); + hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1); + hdev->voice_setting = cpu_to_le16(0x0060); + + cp2.role = 0x00; /* WbDisable */ + cp2.pkt_type = cpu_to_le16(0x0002); + hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2); + + cp3.i2c_enable = 0x00; + cp3.is_master = 0x00; + cp3.pcm_rate = 0x00; + cp3.clock_rate = 0x04; + hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3); + + cp4.sco_routing = 0x00; + cp4.pcm_rate = 0x04; + cp4.frame_type = 0x00; + cp4.sync_mode = 0x00; + cp4.clock_mode = 0x00; + hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4); +} + +void sco_connect_set_wbc(struct hci_dev *hdev) +{ + struct hci_cp_write_voice_setting cp1; + struct hci_cp_bcm_wbs_req cp2; + struct hci_cp_i2c_pcm_req cp3; + struct hci_cp_sco_pcm_req cp4; + + BT_DBG("Setting the WBC params, hdev = %p", hdev); + cp1.voice_setting = cpu_to_le16(0x0003 | 0x0060); + hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1); + hdev->voice_setting = cpu_to_le16(0x0003 | 0x0060); + + cp2.role = 0x01; /* Enable */ + cp2.pkt_type = cpu_to_le16(0x0002); + hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2); + + cp3.i2c_enable = 0x00; + cp3.is_master = 0x00; + cp3.pcm_rate = 0x01; + cp3.clock_rate = 0x04; + hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3); + + cp4.sco_routing = 0x00; + cp4.pcm_rate = 0x04; + cp4.frame_type = 0x00; + cp4.sync_mode = 0x00; + cp4.clock_mode = 0x00; + hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4); +} +#endif + /* ----- SCO interface with lower layer (HCI) ----- */ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) { @@ -1094,8 +1240,14 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) if (sk->sk_state != BT_LISTEN) continue; +#ifdef TIZEN_BT + /* Multiple SCO accept feature */ + if (!bacmp(&sco_pi(sk)->src, bdaddr) || + !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) { +#else if (!bacmp(&sco_pi(sk)->src, &hdev->bdaddr) || !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) { +#endif lm |= HCI_LM_ACCEPT; if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) @@ -1105,6 +1257,36 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) } read_unlock(&sco_sk_list.lock); +#ifdef TIZEN_BT + /* WBC/NBC feature */ + if ((lm & HCI_LM_ACCEPT) && !hci_conn_hash_lookup_sco(hdev)) { + struct hci_conn *hcon_acl; + + hcon_acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); + if (!hcon_acl) { + BT_ERR("ACL doesn't alive. Use 0x%X", + hdev->voice_setting); + + if (hdev->voice_setting == 0x0063) + sco_connect_set_wbc(hdev); + else + sco_connect_set_nbc(hdev); + } else if (hcon_acl->sco_role == MGMT_SCO_ROLE_HANDSFREE) { + BT_DBG("Handsfree SCO 0x%X", hcon_acl->voice_setting); + if (hcon_acl->voice_setting == 0x0063) + sco_connect_set_wbc(hdev); + else + sco_connect_set_nbc(hdev); + } else { + BT_DBG("Gateway SCO 0x%X", hcon_acl->voice_setting); + if (hcon_acl->voice_setting == 0x0063) + sco_connect_set_gw_wbc(hdev); + else + sco_connect_set_gw_nbc(hdev); + } + } +#endif + return lm; } -- 2.7.4