mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 19:21:31 -04:00
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
This commit is contained in:
commit
f5237f278f
249 changed files with 26303 additions and 3190 deletions
|
|
@ -290,7 +290,7 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
|||
goto done;
|
||||
}
|
||||
|
||||
mgr->state = READ_LOC_AMP_INFO;
|
||||
set_bit(READ_LOC_AMP_INFO, &mgr->state);
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
|
||||
|
||||
done:
|
||||
|
|
@ -499,8 +499,16 @@ send_rsp:
|
|||
if (hdev)
|
||||
hci_dev_put(hdev);
|
||||
|
||||
a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp),
|
||||
&rsp);
|
||||
/* Reply error now and success after HCI Write Remote AMP Assoc
|
||||
command complete with success status
|
||||
*/
|
||||
if (rsp.status != A2MP_STATUS_SUCCESS) {
|
||||
a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident,
|
||||
sizeof(rsp), &rsp);
|
||||
} else {
|
||||
set_bit(WRITE_REMOTE_AMP_ASSOC, &mgr->state);
|
||||
mgr->ident = hdr->ident;
|
||||
}
|
||||
|
||||
skb_pull(skb, le16_to_cpu(hdr->len));
|
||||
return 0;
|
||||
|
|
@ -840,7 +848,7 @@ struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
|
|||
|
||||
mutex_lock(&_mgr_list_lock);
|
||||
list_for_each_entry(mgr, &_mgr_list, list) {
|
||||
if (mgr->state == state) {
|
||||
if (test_and_clear_bit(state, &mgr->state)) {
|
||||
amp_mgr_get(mgr);
|
||||
mutex_unlock(&_mgr_list_lock);
|
||||
return mgr;
|
||||
|
|
@ -949,6 +957,32 @@ clean:
|
|||
kfree(req);
|
||||
}
|
||||
|
||||
void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct amp_mgr *mgr;
|
||||
struct a2mp_physlink_rsp rsp;
|
||||
struct hci_conn *hs_hcon;
|
||||
|
||||
mgr = amp_mgr_lookup_by_state(WRITE_REMOTE_AMP_ASSOC);
|
||||
if (!mgr)
|
||||
return;
|
||||
|
||||
hs_hcon = hci_conn_hash_lookup_state(hdev, AMP_LINK, BT_CONNECT);
|
||||
if (!hs_hcon) {
|
||||
rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
|
||||
} else {
|
||||
rsp.remote_id = hs_hcon->remote_id;
|
||||
rsp.status = A2MP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
BT_DBG("%s mgr %p hs_hcon %p status %u", hdev->name, mgr, hs_hcon,
|
||||
status);
|
||||
|
||||
rsp.local_id = hdev->id;
|
||||
a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, mgr->ident, sizeof(rsp), &rsp);
|
||||
amp_mgr_put(mgr);
|
||||
}
|
||||
|
||||
void a2mp_discover_amp(struct l2cap_chan *chan)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
|
|||
|
||||
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||
|
||||
mgr->state = READ_LOC_AMP_ASSOC;
|
||||
set_bit(READ_LOC_AMP_ASSOC, &mgr->state);
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +250,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
|||
cp.len_so_far = cpu_to_le16(0);
|
||||
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||
|
||||
mgr->state = READ_LOC_AMP_ASSOC_FINAL;
|
||||
set_bit(READ_LOC_AMP_ASSOC_FINAL, &mgr->state);
|
||||
|
||||
/* Read Local AMP Assoc final link information data */
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
|
|
@ -317,7 +317,9 @@ void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle)
|
|||
if (!hcon)
|
||||
return;
|
||||
|
||||
amp_write_rem_assoc_frag(hdev, hcon);
|
||||
/* Send A2MP create phylink rsp when all fragments are written */
|
||||
if (amp_write_rem_assoc_frag(hdev, hcon))
|
||||
a2mp_send_create_phy_link_rsp(hdev, 0);
|
||||
}
|
||||
|
||||
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle)
|
||||
|
|
@ -403,26 +405,20 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
|
|||
|
||||
void amp_create_logical_link(struct l2cap_chan *chan)
|
||||
{
|
||||
struct hci_conn *hs_hcon = chan->hs_hcon;
|
||||
struct hci_cp_create_accept_logical_link cp;
|
||||
struct hci_conn *hcon;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
BT_DBG("chan %p", chan);
|
||||
BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst);
|
||||
|
||||
if (!chan->hs_hcon)
|
||||
if (!hs_hcon)
|
||||
return;
|
||||
|
||||
hdev = hci_dev_hold(chan->hs_hcon->hdev);
|
||||
if (!hdev)
|
||||
return;
|
||||
|
||||
BT_DBG("chan %p dst %pMR", chan, chan->conn->dst);
|
||||
|
||||
hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst);
|
||||
if (!hcon)
|
||||
goto done;
|
||||
|
||||
cp.phy_handle = hcon->handle;
|
||||
cp.phy_handle = hs_hcon->handle;
|
||||
|
||||
cp.tx_flow_spec.id = chan->local_id;
|
||||
cp.tx_flow_spec.stype = chan->local_stype;
|
||||
|
|
@ -438,14 +434,13 @@ void amp_create_logical_link(struct l2cap_chan *chan)
|
|||
cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat);
|
||||
cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to);
|
||||
|
||||
if (hcon->out)
|
||||
if (hs_hcon->out)
|
||||
hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
|
||||
&cp);
|
||||
else
|
||||
hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
|
||||
&cp);
|
||||
|
||||
done:
|
||||
hci_dev_put(hdev);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
|
||||
#include "bnep.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1146,7 +1146,8 @@ static void hci_power_on(struct work_struct *work)
|
|||
return;
|
||||
|
||||
if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
|
||||
schedule_delayed_work(&hdev->power_off, HCI_AUTO_OFF_TIMEOUT);
|
||||
queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
|
||||
HCI_AUTO_OFF_TIMEOUT);
|
||||
|
||||
if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
|
||||
mgmt_index_added(hdev);
|
||||
|
|
@ -1182,14 +1183,10 @@ static void hci_discov_off(struct work_struct *work)
|
|||
|
||||
int hci_uuids_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
struct bt_uuid *uuid, *tmp;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->uuids) {
|
||||
struct bt_uuid *uuid;
|
||||
|
||||
uuid = list_entry(p, struct bt_uuid, list);
|
||||
|
||||
list_del(p);
|
||||
list_for_each_entry_safe(uuid, tmp, &hdev->uuids, list) {
|
||||
list_del(&uuid->list);
|
||||
kfree(uuid);
|
||||
}
|
||||
|
||||
|
|
@ -1621,8 +1618,8 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
schedule_delayed_work(&hdev->le_scan_disable,
|
||||
msecs_to_jiffies(timeout));
|
||||
queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
|
||||
msecs_to_jiffies(timeout));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1799,6 +1796,15 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
hdev->req_workqueue = alloc_workqueue(hdev->name,
|
||||
WQ_HIGHPRI | WQ_UNBOUND |
|
||||
WQ_MEM_RECLAIM, 1);
|
||||
if (!hdev->req_workqueue) {
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
error = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
error = hci_add_sysfs(hdev);
|
||||
if (error < 0)
|
||||
goto err_wqueue;
|
||||
|
|
@ -1821,12 +1827,13 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
hci_notify(hdev, HCI_DEV_REG);
|
||||
hci_dev_hold(hdev);
|
||||
|
||||
schedule_work(&hdev->power_on);
|
||||
queue_work(hdev->req_workqueue, &hdev->power_on);
|
||||
|
||||
return id;
|
||||
|
||||
err_wqueue:
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
destroy_workqueue(hdev->req_workqueue);
|
||||
err:
|
||||
ida_simple_remove(&hci_index_ida, hdev->id);
|
||||
write_lock(&hci_dev_list_lock);
|
||||
|
|
@ -1880,6 +1887,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
|||
hci_del_sysfs(hdev);
|
||||
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
destroy_workqueue(hdev->req_workqueue);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_blacklist_clear(hdev);
|
||||
|
|
|
|||
|
|
@ -609,8 +609,17 @@ static void le_setup(struct hci_dev *hdev)
|
|||
/* Read LE Buffer Size */
|
||||
hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
|
||||
|
||||
/* Read LE Local Supported Features */
|
||||
hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL);
|
||||
|
||||
/* Read LE Advertising Channel TX Power */
|
||||
hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
|
||||
|
||||
/* Read LE White List Size */
|
||||
hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL);
|
||||
|
||||
/* Read LE Supported States */
|
||||
hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
|
||||
}
|
||||
|
||||
static void hci_setup(struct hci_dev *hdev)
|
||||
|
|
@ -1090,6 +1099,19 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
|
|||
hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_le_read_local_features(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_local_features *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (!rp->status)
|
||||
memcpy(hdev->le_features, rp->features, 8);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -1290,6 +1312,19 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
|||
}
|
||||
}
|
||||
|
||||
static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_white_list_size *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x size %u", hdev->name, rp->status, rp->size);
|
||||
|
||||
if (!rp->status)
|
||||
hdev->le_white_list_size = rp->size;
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
|
||||
|
|
@ -1314,6 +1349,19 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_supported_states *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (!rp->status)
|
||||
memcpy(hdev->le_states, rp->le_states, 8);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -2628,6 +2676,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_cc_le_read_buffer_size(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_LOCAL_FEATURES:
|
||||
hci_cc_le_read_local_features(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_ADV_TX_POWER:
|
||||
hci_cc_le_read_adv_tx_power(hdev, skb);
|
||||
break;
|
||||
|
|
@ -2664,6 +2716,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_cc_le_set_scan_enable(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_WHITE_LIST_SIZE:
|
||||
hci_cc_le_read_white_list_size(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_LTK_REPLY:
|
||||
hci_cc_le_ltk_reply(hdev, skb);
|
||||
break;
|
||||
|
|
@ -2672,6 +2728,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_cc_le_ltk_neg_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_SUPPORTED_STATES:
|
||||
hci_cc_le_read_supported_states(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_WRITE_LE_HOST_SUPPORTED:
|
||||
hci_cc_write_le_host_supported(hdev, skb);
|
||||
break;
|
||||
|
|
@ -3928,8 +3988,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
void *ptr = &skb->data[1];
|
||||
s8 rssi;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
while (num_reports--) {
|
||||
struct hci_ev_le_advertising_info *ev = ptr;
|
||||
|
||||
|
|
@ -3939,8 +3997,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
ptr += sizeof(*ev) + ev->length + 1;
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
|
@ -461,19 +462,18 @@ static const struct file_operations blacklist_fops = {
|
|||
|
||||
static void print_bt_uuid(struct seq_file *f, u8 *uuid)
|
||||
{
|
||||
__be32 data0, data4;
|
||||
__be16 data1, data2, data3, data5;
|
||||
u32 data0, data5;
|
||||
u16 data1, data2, data3, data4;
|
||||
|
||||
memcpy(&data0, &uuid[0], 4);
|
||||
memcpy(&data1, &uuid[4], 2);
|
||||
memcpy(&data2, &uuid[6], 2);
|
||||
memcpy(&data3, &uuid[8], 2);
|
||||
memcpy(&data4, &uuid[10], 4);
|
||||
memcpy(&data5, &uuid[14], 2);
|
||||
data5 = get_unaligned_le32(uuid);
|
||||
data4 = get_unaligned_le16(uuid + 4);
|
||||
data3 = get_unaligned_le16(uuid + 6);
|
||||
data2 = get_unaligned_le16(uuid + 8);
|
||||
data1 = get_unaligned_le16(uuid + 10);
|
||||
data0 = get_unaligned_le32(uuid + 12);
|
||||
|
||||
seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n",
|
||||
ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3),
|
||||
ntohl(data4), ntohs(data5));
|
||||
seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
|
||||
data0, data1, data2, data3, data4, data5);
|
||||
}
|
||||
|
||||
static int uuids_show(struct seq_file *f, void *p)
|
||||
|
|
|
|||
|
|
@ -1527,17 +1527,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
|||
BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
|
||||
|
||||
switch (hcon->type) {
|
||||
case AMP_LINK:
|
||||
conn->mtu = hcon->hdev->block_mtu;
|
||||
break;
|
||||
|
||||
case LE_LINK:
|
||||
if (hcon->hdev->le_mtu) {
|
||||
conn->mtu = hcon->hdev->le_mtu;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
conn->mtu = hcon->hdev->acl_mtu;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
bool enable_hs;
|
||||
|
||||
#define MGMT_VERSION 1
|
||||
#define MGMT_REVISION 2
|
||||
#define MGMT_REVISION 3
|
||||
|
||||
static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_READ_INDEX_LIST,
|
||||
|
|
@ -435,35 +435,117 @@ static u32 get_current_settings(struct hci_dev *hdev)
|
|||
|
||||
#define PNP_INFO_SVCLASS_ID 0x1200
|
||||
|
||||
static u8 bluetooth_base_uuid[] = {
|
||||
0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static u16 get_uuid16(u8 *uuid128)
|
||||
static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
|
||||
{
|
||||
u32 val;
|
||||
int i;
|
||||
u8 *ptr = data, *uuids_start = NULL;
|
||||
struct bt_uuid *uuid;
|
||||
|
||||
for (i = 0; i < 12; i++) {
|
||||
if (bluetooth_base_uuid[i] != uuid128[i])
|
||||
return 0;
|
||||
if (len < 4)
|
||||
return ptr;
|
||||
|
||||
list_for_each_entry(uuid, &hdev->uuids, list) {
|
||||
u16 uuid16;
|
||||
|
||||
if (uuid->size != 16)
|
||||
continue;
|
||||
|
||||
uuid16 = get_unaligned_le16(&uuid->uuid[12]);
|
||||
if (uuid16 < 0x1100)
|
||||
continue;
|
||||
|
||||
if (uuid16 == PNP_INFO_SVCLASS_ID)
|
||||
continue;
|
||||
|
||||
if (!uuids_start) {
|
||||
uuids_start = ptr;
|
||||
uuids_start[0] = 1;
|
||||
uuids_start[1] = EIR_UUID16_ALL;
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
/* Stop if not enough space to put next UUID */
|
||||
if ((ptr - data) + sizeof(u16) > len) {
|
||||
uuids_start[1] = EIR_UUID16_SOME;
|
||||
break;
|
||||
}
|
||||
|
||||
*ptr++ = (uuid16 & 0x00ff);
|
||||
*ptr++ = (uuid16 & 0xff00) >> 8;
|
||||
uuids_start[0] += sizeof(uuid16);
|
||||
}
|
||||
|
||||
val = get_unaligned_le32(&uuid128[12]);
|
||||
if (val > 0xffff)
|
||||
return 0;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return (u16) val;
|
||||
static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
|
||||
{
|
||||
u8 *ptr = data, *uuids_start = NULL;
|
||||
struct bt_uuid *uuid;
|
||||
|
||||
if (len < 6)
|
||||
return ptr;
|
||||
|
||||
list_for_each_entry(uuid, &hdev->uuids, list) {
|
||||
if (uuid->size != 32)
|
||||
continue;
|
||||
|
||||
if (!uuids_start) {
|
||||
uuids_start = ptr;
|
||||
uuids_start[0] = 1;
|
||||
uuids_start[1] = EIR_UUID32_ALL;
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
/* Stop if not enough space to put next UUID */
|
||||
if ((ptr - data) + sizeof(u32) > len) {
|
||||
uuids_start[1] = EIR_UUID32_SOME;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(ptr, &uuid->uuid[12], sizeof(u32));
|
||||
ptr += sizeof(u32);
|
||||
uuids_start[0] += sizeof(u32);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
|
||||
{
|
||||
u8 *ptr = data, *uuids_start = NULL;
|
||||
struct bt_uuid *uuid;
|
||||
|
||||
if (len < 18)
|
||||
return ptr;
|
||||
|
||||
list_for_each_entry(uuid, &hdev->uuids, list) {
|
||||
if (uuid->size != 128)
|
||||
continue;
|
||||
|
||||
if (!uuids_start) {
|
||||
uuids_start = ptr;
|
||||
uuids_start[0] = 1;
|
||||
uuids_start[1] = EIR_UUID128_ALL;
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
/* Stop if not enough space to put next UUID */
|
||||
if ((ptr - data) + 16 > len) {
|
||||
uuids_start[1] = EIR_UUID128_SOME;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(ptr, uuid->uuid, 16);
|
||||
ptr += 16;
|
||||
uuids_start[0] += 16;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void create_eir(struct hci_dev *hdev, u8 *data)
|
||||
{
|
||||
u8 *ptr = data;
|
||||
u16 eir_len = 0;
|
||||
u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
|
||||
int i, truncated = 0;
|
||||
struct bt_uuid *uuid;
|
||||
size_t name_len;
|
||||
|
||||
name_len = strlen(hdev->dev_name);
|
||||
|
|
@ -481,7 +563,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
|
|||
|
||||
memcpy(ptr + 2, hdev->dev_name, name_len);
|
||||
|
||||
eir_len += (name_len + 2);
|
||||
ptr += (name_len + 2);
|
||||
}
|
||||
|
||||
|
|
@ -490,7 +571,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
|
|||
ptr[1] = EIR_TX_POWER;
|
||||
ptr[2] = (u8) hdev->inq_tx_power;
|
||||
|
||||
eir_len += 3;
|
||||
ptr += 3;
|
||||
}
|
||||
|
||||
|
|
@ -503,60 +583,12 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
|
|||
put_unaligned_le16(hdev->devid_product, ptr + 6);
|
||||
put_unaligned_le16(hdev->devid_version, ptr + 8);
|
||||
|
||||
eir_len += 10;
|
||||
ptr += 10;
|
||||
}
|
||||
|
||||
memset(uuid16_list, 0, sizeof(uuid16_list));
|
||||
|
||||
/* Group all UUID16 types */
|
||||
list_for_each_entry(uuid, &hdev->uuids, list) {
|
||||
u16 uuid16;
|
||||
|
||||
uuid16 = get_uuid16(uuid->uuid);
|
||||
if (uuid16 == 0)
|
||||
return;
|
||||
|
||||
if (uuid16 < 0x1100)
|
||||
continue;
|
||||
|
||||
if (uuid16 == PNP_INFO_SVCLASS_ID)
|
||||
continue;
|
||||
|
||||
/* Stop if not enough space to put next UUID */
|
||||
if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
|
||||
truncated = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for duplicates */
|
||||
for (i = 0; uuid16_list[i] != 0; i++)
|
||||
if (uuid16_list[i] == uuid16)
|
||||
break;
|
||||
|
||||
if (uuid16_list[i] == 0) {
|
||||
uuid16_list[i] = uuid16;
|
||||
eir_len += sizeof(u16);
|
||||
}
|
||||
}
|
||||
|
||||
if (uuid16_list[0] != 0) {
|
||||
u8 *length = ptr;
|
||||
|
||||
/* EIR Data type */
|
||||
ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
|
||||
|
||||
ptr += 2;
|
||||
eir_len += 2;
|
||||
|
||||
for (i = 0; uuid16_list[i] != 0; i++) {
|
||||
*ptr++ = (uuid16_list[i] & 0x00ff);
|
||||
*ptr++ = (uuid16_list[i] & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
/* EIR Data length */
|
||||
*length = (i * sizeof(u16)) + 1;
|
||||
}
|
||||
ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
|
||||
ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
|
||||
ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
|
||||
}
|
||||
|
||||
static int update_eir(struct hci_dev *hdev)
|
||||
|
|
@ -728,13 +760,9 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
|
|||
void *data),
|
||||
void *data)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->mgmt_pending) {
|
||||
struct pending_cmd *cmd;
|
||||
|
||||
cmd = list_entry(p, struct pending_cmd, list);
|
||||
struct pending_cmd *cmd, *tmp;
|
||||
|
||||
list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
|
||||
if (opcode > 0 && cmd->opcode != opcode)
|
||||
continue;
|
||||
|
||||
|
|
@ -777,14 +805,19 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
BT_DBG("request for %s", hdev->name);
|
||||
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
|
||||
cancel_delayed_work(&hdev->power_off);
|
||||
|
||||
if (cp->val) {
|
||||
err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
|
||||
mgmt_powered(hdev, 1);
|
||||
mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev,
|
||||
data, len);
|
||||
err = mgmt_powered(hdev, 1);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
|
@ -807,9 +840,9 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
}
|
||||
|
||||
if (cp->val)
|
||||
schedule_work(&hdev->power_on);
|
||||
queue_work(hdev->req_workqueue, &hdev->power_on);
|
||||
else
|
||||
schedule_work(&hdev->power_off.work);
|
||||
queue_work(hdev->req_workqueue, &hdev->power_off.work);
|
||||
|
||||
err = 0;
|
||||
|
||||
|
|
@ -872,6 +905,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
timeout = __le16_to_cpu(cp->timeout);
|
||||
if (!cp->val && timeout > 0)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
|
||||
|
|
@ -971,6 +1008,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
|
|
@ -1041,6 +1082,10 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
BT_DBG("request for %s", hdev->name);
|
||||
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_PAIRABLE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (cp->val)
|
||||
|
|
@ -1073,6 +1118,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
|
|
@ -1133,13 +1182,15 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||
|
||||
BT_DBG("request for %s", hdev->name);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
if (!lmp_ssp_capable(hdev))
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (!lmp_ssp_capable(hdev)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
goto failed;
|
||||
}
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
val = !!cp->val;
|
||||
|
||||
|
|
@ -1199,6 +1250,10 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||
return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
if (cp->val)
|
||||
set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
|
||||
else
|
||||
|
|
@ -1217,13 +1272,15 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||
|
||||
BT_DBG("request for %s", hdev->name);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
if (!lmp_le_capable(hdev))
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (!lmp_le_capable(hdev)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
goto unlock;
|
||||
}
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
val = !!cp->val;
|
||||
enabled = lmp_host_le_capable(hdev);
|
||||
|
|
@ -1275,6 +1332,25 @@ unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
static const u8 bluetooth_base_uuid[] = {
|
||||
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static u8 get_uuid_size(const u8 *uuid)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (memcmp(uuid, bluetooth_base_uuid, 12))
|
||||
return 128;
|
||||
|
||||
val = get_unaligned_le32(&uuid[12]);
|
||||
if (val > 0xffff)
|
||||
return 32;
|
||||
|
||||
return 16;
|
||||
}
|
||||
|
||||
static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
{
|
||||
struct mgmt_cp_add_uuid *cp = data;
|
||||
|
|
@ -1300,8 +1376,9 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||
|
||||
memcpy(uuid->uuid, cp->uuid, 16);
|
||||
uuid->svc_hint = cp->svc_hint;
|
||||
uuid->size = get_uuid_size(cp->uuid);
|
||||
|
||||
list_add(&uuid->list, &hdev->uuids);
|
||||
list_add_tail(&uuid->list, &hdev->uuids);
|
||||
|
||||
err = update_class(hdev);
|
||||
if (err < 0)
|
||||
|
|
@ -1332,7 +1409,8 @@ static bool enable_service_cache(struct hci_dev *hdev)
|
|||
return false;
|
||||
|
||||
if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
|
||||
schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT);
|
||||
queue_delayed_work(hdev->workqueue, &hdev->service_cache,
|
||||
CACHE_TIMEOUT);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1344,7 +1422,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
{
|
||||
struct mgmt_cp_remove_uuid *cp = data;
|
||||
struct pending_cmd *cmd;
|
||||
struct list_head *p, *n;
|
||||
struct bt_uuid *match, *tmp;
|
||||
u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
int err, found;
|
||||
|
||||
|
|
@ -1372,9 +1450,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
found = 0;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->uuids) {
|
||||
struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
|
||||
|
||||
list_for_each_entry_safe(match, tmp, &hdev->uuids, list) {
|
||||
if (memcmp(match->uuid, cp->uuid, 16) != 0)
|
||||
continue;
|
||||
|
||||
|
|
@ -1422,13 +1498,19 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
BT_DBG("request for %s", hdev->name);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
if (!lmp_bredr_capable(hdev))
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto unlock;
|
||||
}
|
||||
if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags))
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
|
||||
MGMT_STATUS_BUSY);
|
||||
|
||||
if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hdev->major_class = cp->major;
|
||||
hdev->minor_class = cp->minor;
|
||||
|
|
@ -1483,9 +1565,21 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
if (cp->debug_keys != 0x00 && cp->debug_keys != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys,
|
||||
key_count);
|
||||
|
||||
for (i = 0; i < key_count; i++) {
|
||||
struct mgmt_link_key_info *key = &cp->keys[i];
|
||||
|
||||
if (key->addr.type != BDADDR_BREDR)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hci_link_keys_clear(hdev);
|
||||
|
|
@ -1533,12 +1627,22 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
struct hci_conn *conn;
|
||||
int err;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
|
||||
rp.addr.type = cp->addr.type;
|
||||
|
||||
if (!bdaddr_type_is_valid(cp->addr.type))
|
||||
return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
if (cp->disconnect != 0x00 && cp->disconnect != 0x01)
|
||||
return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
|
||||
MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
|
||||
|
|
@ -1596,6 +1700,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
u16 len)
|
||||
{
|
||||
struct mgmt_cp_disconnect *cp = data;
|
||||
struct mgmt_rp_disconnect rp;
|
||||
struct hci_cp_disconnect dc;
|
||||
struct pending_cmd *cmd;
|
||||
struct hci_conn *conn;
|
||||
|
|
@ -1603,17 +1708,26 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
BT_DBG("");
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
|
||||
rp.addr.type = cp->addr.type;
|
||||
|
||||
if (!bdaddr_type_is_valid(cp->addr.type))
|
||||
return cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
|
||||
MGMT_STATUS_NOT_POWERED);
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
|
||||
MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
|
||||
MGMT_STATUS_BUSY);
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
|
||||
MGMT_STATUS_BUSY, &rp, sizeof(rp));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
|
|
@ -1624,8 +1738,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
|
||||
|
||||
if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
|
||||
MGMT_STATUS_NOT_CONNECTED);
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
|
||||
MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
|
|
@ -1903,11 +2017,20 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
BT_DBG("");
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
|
||||
rp.addr.type = cp->addr.type;
|
||||
|
||||
if (!bdaddr_type_is_valid(cp->addr.type))
|
||||
return cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
|
||||
MGMT_STATUS_NOT_POWERED);
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
|
||||
MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
|
@ -1924,10 +2047,6 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr,
|
||||
cp->addr.type, sec_level, auth_type);
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
|
||||
rp.addr.type = cp->addr.type;
|
||||
|
||||
if (IS_ERR(conn)) {
|
||||
int status;
|
||||
|
||||
|
|
@ -2254,24 +2373,16 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
|
||||
MGMT_STATUS_NOT_POWERED, &cp->addr,
|
||||
sizeof(cp->addr));
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
|
||||
cp->randomizer);
|
||||
if (err < 0)
|
||||
status = MGMT_STATUS_FAILED;
|
||||
else
|
||||
status = 0;
|
||||
status = MGMT_STATUS_SUCCESS;
|
||||
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2287,24 +2398,15 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
err = cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
MGMT_STATUS_NOT_POWERED, &cp->addr,
|
||||
sizeof(cp->addr));
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
|
||||
if (err < 0)
|
||||
status = MGMT_STATUS_INVALID_PARAMS;
|
||||
else
|
||||
status = 0;
|
||||
status = MGMT_STATUS_SUCCESS;
|
||||
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
status, &cp->addr, sizeof(cp->addr));
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2365,31 +2467,45 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
|||
|
||||
switch (hdev->discovery.type) {
|
||||
case DISCOV_TYPE_BREDR:
|
||||
if (lmp_bredr_capable(hdev))
|
||||
err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
|
||||
else
|
||||
err = -ENOTSUPP;
|
||||
if (!lmp_bredr_capable(hdev)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
|
||||
break;
|
||||
|
||||
case DISCOV_TYPE_LE:
|
||||
if (lmp_host_le_capable(hdev))
|
||||
err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
|
||||
LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
|
||||
else
|
||||
err = -ENOTSUPP;
|
||||
if (!lmp_host_le_capable(hdev)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
|
||||
LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
|
||||
break;
|
||||
|
||||
case DISCOV_TYPE_INTERLEAVED:
|
||||
if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev))
|
||||
err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
|
||||
LE_SCAN_WIN,
|
||||
LE_SCAN_TIMEOUT_BREDR_LE);
|
||||
else
|
||||
err = -ENOTSUPP;
|
||||
if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, LE_SCAN_WIN,
|
||||
LE_SCAN_TIMEOUT_BREDR_LE);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
|
|
@ -2510,7 +2626,8 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
hci_inquiry_cache_update_resolve(hdev, e);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 0, &cp->addr,
|
||||
sizeof(cp->addr));
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
@ -2526,13 +2643,18 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!bdaddr_type_is_valid(cp->addr.type))
|
||||
return cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
|
||||
if (err < 0)
|
||||
status = MGMT_STATUS_FAILED;
|
||||
else
|
||||
status = 0;
|
||||
status = MGMT_STATUS_SUCCESS;
|
||||
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
|
|
@ -2551,13 +2673,18 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!bdaddr_type_is_valid(cp->addr.type))
|
||||
return cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
|
||||
if (err < 0)
|
||||
status = MGMT_STATUS_INVALID_PARAMS;
|
||||
else
|
||||
status = 0;
|
||||
status = MGMT_STATUS_SUCCESS;
|
||||
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
|
|
@ -2612,6 +2739,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
|
|||
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
if (!hdev_is_powered(hdev))
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
|
||||
MGMT_STATUS_NOT_POWERED);
|
||||
|
|
@ -2659,12 +2790,23 @@ done:
|
|||
return err;
|
||||
}
|
||||
|
||||
static bool ltk_is_valid(struct mgmt_ltk_info *key)
|
||||
{
|
||||
if (key->authenticated != 0x00 && key->authenticated != 0x01)
|
||||
return false;
|
||||
if (key->master != 0x00 && key->master != 0x01)
|
||||
return false;
|
||||
if (!bdaddr_type_is_le(key->addr.type))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
|
||||
void *cp_data, u16 len)
|
||||
{
|
||||
struct mgmt_cp_load_long_term_keys *cp = cp_data;
|
||||
u16 key_count, expected_len;
|
||||
int i;
|
||||
int i, err;
|
||||
|
||||
key_count = __le16_to_cpu(cp->key_count);
|
||||
|
||||
|
|
@ -2674,11 +2816,20 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
|
|||
BT_ERR("load_keys: expected %u bytes, got %u bytes",
|
||||
len, expected_len);
|
||||
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
|
||||
EINVAL);
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
BT_DBG("%s key_count %u", hdev->name, key_count);
|
||||
|
||||
for (i = 0; i < key_count; i++) {
|
||||
struct mgmt_ltk_info *key = &cp->keys[i];
|
||||
|
||||
if (!ltk_is_valid(key))
|
||||
return cmd_status(sk, hdev->id,
|
||||
MGMT_OP_LOAD_LONG_TERM_KEYS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hci_smp_ltks_clear(hdev);
|
||||
|
|
@ -2698,9 +2849,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
|
|||
key->enc_size, key->ediv, key->rand);
|
||||
}
|
||||
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
|
||||
NULL, 0);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct mgmt_handler {
|
||||
|
|
@ -2915,6 +3069,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
|
|||
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
|
||||
|
||||
if (powered) {
|
||||
u8 link_sec;
|
||||
|
||||
if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
|
||||
!lmp_host_ssp_capable(hdev)) {
|
||||
u8 ssp = 1;
|
||||
|
|
@ -2938,6 +3094,11 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
|
|||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
|
||||
if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE,
|
||||
sizeof(link_sec), &link_sec);
|
||||
|
||||
if (lmp_bredr_capable(hdev)) {
|
||||
set_bredr_scan(hdev);
|
||||
update_class(hdev);
|
||||
|
|
@ -2946,7 +3107,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
|
|||
}
|
||||
} else {
|
||||
u8 status = MGMT_STATUS_NOT_POWERED;
|
||||
u8 zero_cod[] = { 0, 0, 0 };
|
||||
|
||||
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
|
||||
|
||||
if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
|
||||
mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
|
||||
zero_cod, sizeof(zero_cod), NULL);
|
||||
}
|
||||
|
||||
err = new_settings(hdev, match.sk);
|
||||
|
|
|
|||
|
|
@ -900,8 +900,6 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
sco_conn_lock(conn);
|
||||
|
||||
if (sk) {
|
||||
sco_sock_clear_timer(sk);
|
||||
bh_lock_sock(sk);
|
||||
|
|
@ -909,9 +907,13 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||
sk->sk_state_change(sk);
|
||||
bh_unlock_sock(sk);
|
||||
} else {
|
||||
sco_conn_lock(conn);
|
||||
|
||||
parent = sco_get_sock_listen(conn->src);
|
||||
if (!parent)
|
||||
goto done;
|
||||
if (!parent) {
|
||||
sco_conn_unlock(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
bh_lock_sock(parent);
|
||||
|
||||
|
|
@ -919,7 +921,8 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||
BTPROTO_SCO, GFP_ATOMIC);
|
||||
if (!sk) {
|
||||
bh_unlock_sock(parent);
|
||||
goto done;
|
||||
sco_conn_unlock(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
sco_sock_init(sk, parent);
|
||||
|
|
@ -939,10 +942,9 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||
parent->sk_data_ready(parent, 1);
|
||||
|
||||
bh_unlock_sock(parent);
|
||||
}
|
||||
|
||||
done:
|
||||
sco_conn_unlock(conn);
|
||||
sco_conn_unlock(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----- SCO interface with lower layer (HCI) ----- */
|
||||
|
|
|
|||
Loading…
Reference in a new issue