Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

This commit is contained in:
John W. Linville 2013-01-30 14:21:04 -05:00
commit 0f496df2d9
13 changed files with 375 additions and 120 deletions

View file

@ -23,6 +23,7 @@ enum amp_mgr_state {
READ_LOC_AMP_INFO, READ_LOC_AMP_INFO,
READ_LOC_AMP_ASSOC, READ_LOC_AMP_ASSOC,
READ_LOC_AMP_ASSOC_FINAL, READ_LOC_AMP_ASSOC_FINAL,
WRITE_REMOTE_AMP_ASSOC,
}; };
struct amp_mgr { struct amp_mgr {
@ -33,7 +34,7 @@ struct amp_mgr {
struct kref kref; struct kref kref;
__u8 ident; __u8 ident;
__u8 handle; __u8 handle;
enum amp_mgr_state state; unsigned long state;
unsigned long flags; unsigned long flags;
struct list_head amp_ctrls; struct list_head amp_ctrls;
@ -144,5 +145,6 @@ void a2mp_discover_amp(struct l2cap_chan *chan);
void a2mp_send_getinfo_rsp(struct hci_dev *hdev); void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status); void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
#endif /* __A2MP_H */ #endif /* __A2MP_H */

View file

@ -166,6 +166,29 @@ typedef struct {
#define BDADDR_LE_PUBLIC 0x01 #define BDADDR_LE_PUBLIC 0x01
#define BDADDR_LE_RANDOM 0x02 #define BDADDR_LE_RANDOM 0x02
static inline bool bdaddr_type_is_valid(__u8 type)
{
switch (type) {
case BDADDR_BREDR:
case BDADDR_LE_PUBLIC:
case BDADDR_LE_RANDOM:
return true;
}
return false;
}
static inline bool bdaddr_type_is_le(__u8 type)
{
switch (type) {
case BDADDR_LE_PUBLIC:
case BDADDR_LE_RANDOM:
return true;
}
return false;
}
#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} }) #define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} })
#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} }) #define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} })

View file

@ -943,6 +943,12 @@ struct hci_rp_le_read_buffer_size {
__u8 le_max_pkt; __u8 le_max_pkt;
} __packed; } __packed;
#define HCI_OP_LE_READ_LOCAL_FEATURES 0x2003
struct hci_rp_le_read_local_features {
__u8 status;
__u8 features[8];
} __packed;
#define HCI_OP_LE_READ_ADV_TX_POWER 0x2007 #define HCI_OP_LE_READ_ADV_TX_POWER 0x2007
struct hci_rp_le_read_adv_tx_power { struct hci_rp_le_read_adv_tx_power {
__u8 status; __u8 status;
@ -995,6 +1001,12 @@ struct hci_cp_le_create_conn {
#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e #define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e
#define HCI_OP_LE_READ_WHITE_LIST_SIZE 0x200f
struct hci_rp_le_read_white_list_size {
__u8 status;
__u8 size;
} __packed;
#define HCI_OP_LE_CONN_UPDATE 0x2013 #define HCI_OP_LE_CONN_UPDATE 0x2013
struct hci_cp_le_conn_update { struct hci_cp_le_conn_update {
__le16 handle; __le16 handle;
@ -1033,6 +1045,12 @@ struct hci_rp_le_ltk_neg_reply {
__le16 handle; __le16 handle;
} __packed; } __packed;
#define HCI_OP_LE_READ_SUPPORTED_STATES 0x201c
struct hci_rp_le_read_supported_states {
__u8 status;
__u8 le_states[8];
} __packed;
/* ---- HCI Events ---- */ /* ---- HCI Events ---- */
#define HCI_EV_INQUIRY_COMPLETE 0x01 #define HCI_EV_INQUIRY_COMPLETE 0x01

View file

@ -152,6 +152,9 @@ struct hci_dev {
__u8 minor_class; __u8 minor_class;
__u8 features[8]; __u8 features[8];
__u8 host_features[8]; __u8 host_features[8];
__u8 le_features[8];
__u8 le_white_list_size;
__u8 le_states[8];
__u8 commands[64]; __u8 commands[64];
__u8 hci_ver; __u8 hci_ver;
__u16 hci_rev; __u16 hci_rev;
@ -216,6 +219,7 @@ struct hci_dev {
unsigned long le_last_tx; unsigned long le_last_tx;
struct workqueue_struct *workqueue; struct workqueue_struct *workqueue;
struct workqueue_struct *req_workqueue;
struct work_struct power_on; struct work_struct power_on;
struct delayed_work power_off; struct delayed_work power_off;

View file

@ -496,7 +496,6 @@ struct l2cap_chan {
__u16 frames_sent; __u16 frames_sent;
__u16 unacked_frames; __u16 unacked_frames;
__u8 retry_count; __u8 retry_count;
__u16 srej_queue_next;
__u16 sdu_len; __u16 sdu_len;
struct sk_buff *sdu; struct sk_buff *sdu;
struct sk_buff *sdu_last_frag; struct sk_buff *sdu_last_frag;

View file

@ -290,7 +290,7 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
goto done; 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); hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
done: done:
@ -499,8 +499,16 @@ send_rsp:
if (hdev) if (hdev)
hci_dev_put(hdev); hci_dev_put(hdev);
a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp), /* Reply error now and success after HCI Write Remote AMP Assoc
&rsp); 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)); skb_pull(skb, le16_to_cpu(hdr->len));
return 0; return 0;
@ -840,7 +848,7 @@ struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
mutex_lock(&amp_mgr_list_lock); mutex_lock(&amp_mgr_list_lock);
list_for_each_entry(mgr, &amp_mgr_list, list) { list_for_each_entry(mgr, &amp_mgr_list, list) {
if (mgr->state == state) { if (test_and_clear_bit(state, &mgr->state)) {
amp_mgr_get(mgr); amp_mgr_get(mgr);
mutex_unlock(&amp_mgr_list_lock); mutex_unlock(&amp_mgr_list_lock);
return mgr; return mgr;
@ -949,6 +957,32 @@ clean:
kfree(req); 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) void a2mp_discover_amp(struct l2cap_chan *chan)
{ {
struct l2cap_conn *conn = chan->conn; struct l2cap_conn *conn = chan->conn;

View file

@ -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); 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); 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.len_so_far = cpu_to_le16(0);
cp.max_len = cpu_to_le16(hdev->amp_assoc_size); 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 */ /* Read Local AMP Assoc final link information data */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); 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) if (!hcon)
return; 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) 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) 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_cp_create_accept_logical_link cp;
struct hci_conn *hcon;
struct hci_dev *hdev; 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; return;
hdev = hci_dev_hold(chan->hs_hcon->hdev); hdev = hci_dev_hold(chan->hs_hcon->hdev);
if (!hdev) if (!hdev)
return; return;
BT_DBG("chan %p dst %pMR", chan, chan->conn->dst); cp.phy_handle = hs_hcon->handle;
hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst);
if (!hcon)
goto done;
cp.phy_handle = hcon->handle;
cp.tx_flow_spec.id = chan->local_id; cp.tx_flow_spec.id = chan->local_id;
cp.tx_flow_spec.stype = chan->local_stype; 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.acc_lat = cpu_to_le32(chan->remote_acc_lat);
cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to); 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), hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
&cp); &cp);
else else
hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp), hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
&cp); &cp);
done:
hci_dev_put(hdev); hci_dev_put(hdev);
} }

View file

@ -33,7 +33,6 @@
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include "bnep.h" #include "bnep.h"

View file

@ -1146,7 +1146,8 @@ static void hci_power_on(struct work_struct *work)
return; return;
if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) 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)) if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
mgmt_index_added(hdev); mgmt_index_added(hdev);
@ -1621,7 +1622,7 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
if (err < 0) if (err < 0)
return err; return err;
schedule_delayed_work(&hdev->le_scan_disable, queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
msecs_to_jiffies(timeout)); msecs_to_jiffies(timeout));
return 0; return 0;
@ -1799,6 +1800,15 @@ int hci_register_dev(struct hci_dev *hdev)
goto err; 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); error = hci_add_sysfs(hdev);
if (error < 0) if (error < 0)
goto err_wqueue; goto err_wqueue;
@ -1821,12 +1831,13 @@ int hci_register_dev(struct hci_dev *hdev)
hci_notify(hdev, HCI_DEV_REG); hci_notify(hdev, HCI_DEV_REG);
hci_dev_hold(hdev); hci_dev_hold(hdev);
schedule_work(&hdev->power_on); queue_work(hdev->req_workqueue, &hdev->power_on);
return id; return id;
err_wqueue: err_wqueue:
destroy_workqueue(hdev->workqueue); destroy_workqueue(hdev->workqueue);
destroy_workqueue(hdev->req_workqueue);
err: err:
ida_simple_remove(&hci_index_ida, hdev->id); ida_simple_remove(&hci_index_ida, hdev->id);
write_lock(&hci_dev_list_lock); write_lock(&hci_dev_list_lock);
@ -1880,6 +1891,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_del_sysfs(hdev); hci_del_sysfs(hdev);
destroy_workqueue(hdev->workqueue); destroy_workqueue(hdev->workqueue);
destroy_workqueue(hdev->req_workqueue);
hci_dev_lock(hdev); hci_dev_lock(hdev);
hci_blacklist_clear(hdev); hci_blacklist_clear(hdev);

View file

@ -609,8 +609,17 @@ static void le_setup(struct hci_dev *hdev)
/* Read LE Buffer Size */ /* Read LE Buffer Size */
hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); 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 */ /* Read LE Advertising Channel TX Power */
hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); 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) 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); 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, static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
struct sk_buff *skb) 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) 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; 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); 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, static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
struct sk_buff *skb) 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); hci_cc_le_read_buffer_size(hdev, skb);
break; 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: case HCI_OP_LE_READ_ADV_TX_POWER:
hci_cc_le_read_adv_tx_power(hdev, skb); hci_cc_le_read_adv_tx_power(hdev, skb);
break; 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); hci_cc_le_set_scan_enable(hdev, skb);
break; 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: case HCI_OP_LE_LTK_REPLY:
hci_cc_le_ltk_reply(hdev, skb); hci_cc_le_ltk_reply(hdev, skb);
break; 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); hci_cc_le_ltk_neg_reply(hdev, skb);
break; break;
case HCI_OP_LE_READ_SUPPORTED_STATES:
hci_cc_le_read_supported_states(hdev, skb);
break;
case HCI_OP_WRITE_LE_HOST_SUPPORTED: case HCI_OP_WRITE_LE_HOST_SUPPORTED:
hci_cc_write_le_host_supported(hdev, skb); hci_cc_write_le_host_supported(hdev, skb);
break; break;

View file

@ -2,6 +2,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.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) static void print_bt_uuid(struct seq_file *f, u8 *uuid)
{ {
__be32 data0, data4; u32 data0, data5;
__be16 data1, data2, data3, data5; u16 data1, data2, data3, data4;
memcpy(&data0, &uuid[0], 4); data5 = get_unaligned_le32(uuid);
memcpy(&data1, &uuid[4], 2); data4 = get_unaligned_le16(uuid + 4);
memcpy(&data2, &uuid[6], 2); data3 = get_unaligned_le16(uuid + 6);
memcpy(&data3, &uuid[8], 2); data2 = get_unaligned_le16(uuid + 8);
memcpy(&data4, &uuid[10], 4); data1 = get_unaligned_le16(uuid + 10);
memcpy(&data5, &uuid[14], 2); data0 = get_unaligned_le32(uuid + 12);
seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n", seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3), data0, data1, data2, data3, data4, data5);
ntohl(data4), ntohs(data5));
} }
static int uuids_show(struct seq_file *f, void *p) static int uuids_show(struct seq_file *f, void *p)

View file

@ -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); BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
switch (hcon->type) { switch (hcon->type) {
case AMP_LINK:
conn->mtu = hcon->hdev->block_mtu;
break;
case LE_LINK: case LE_LINK:
if (hcon->hdev->le_mtu) { if (hcon->hdev->le_mtu) {
conn->mtu = hcon->hdev->le_mtu; conn->mtu = hcon->hdev->le_mtu;
break; break;
} }
/* fall through */ /* fall through */
default: default:
conn->mtu = hcon->hdev->acl_mtu; conn->mtu = hcon->hdev->acl_mtu;
break; break;

View file

@ -777,14 +777,19 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name); 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); hci_dev_lock(hdev);
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
cancel_delayed_work(&hdev->power_off); cancel_delayed_work(&hdev->power_off);
if (cp->val) { if (cp->val) {
err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev,
mgmt_powered(hdev, 1); data, len);
err = mgmt_powered(hdev, 1);
goto failed; goto failed;
} }
} }
@ -807,9 +812,9 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
} }
if (cp->val) if (cp->val)
schedule_work(&hdev->power_on); queue_work(hdev->req_workqueue, &hdev->power_on);
else else
schedule_work(&hdev->power_off.work); queue_work(hdev->req_workqueue, &hdev->power_off.work);
err = 0; err = 0;
@ -872,6 +877,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
MGMT_STATUS_NOT_SUPPORTED); 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); timeout = __le16_to_cpu(cp->timeout);
if (!cp->val && timeout > 0) if (!cp->val && timeout > 0)
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
@ -971,6 +980,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
MGMT_STATUS_NOT_SUPPORTED); 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); hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) { if (!hdev_is_powered(hdev)) {
@ -1041,6 +1054,10 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name); 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); hci_dev_lock(hdev);
if (cp->val) if (cp->val)
@ -1073,6 +1090,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, return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
MGMT_STATUS_NOT_SUPPORTED); 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); hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) { if (!hdev_is_powered(hdev)) {
@ -1133,13 +1154,15 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
BT_DBG("request for %s", hdev->name); 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,
if (!lmp_ssp_capable(hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
MGMT_STATUS_NOT_SUPPORTED); 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; val = !!cp->val;
@ -1199,6 +1222,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, return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
MGMT_STATUS_NOT_SUPPORTED); 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) if (cp->val)
set_bit(HCI_HS_ENABLED, &hdev->dev_flags); set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
else else
@ -1217,13 +1244,15 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
BT_DBG("request for %s", hdev->name); 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,
if (!lmp_le_capable(hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
MGMT_STATUS_NOT_SUPPORTED); 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; val = !!cp->val;
enabled = lmp_host_le_capable(hdev); enabled = lmp_host_le_capable(hdev);
@ -1332,7 +1361,8 @@ static bool enable_service_cache(struct hci_dev *hdev)
return false; return false;
if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { 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; return true;
} }
@ -1422,13 +1452,19 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name); 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)) { if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags))
err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
MGMT_STATUS_BUSY); MGMT_STATUS_BUSY);
goto unlock;
} 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->major_class = cp->major;
hdev->minor_class = cp->minor; hdev->minor_class = cp->minor;
@ -1483,9 +1519,21 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
MGMT_STATUS_INVALID_PARAMS); 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, BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys,
key_count); 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_dev_lock(hdev);
hci_link_keys_clear(hdev); hci_link_keys_clear(hdev);
@ -1533,12 +1581,22 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
struct hci_conn *conn; struct hci_conn *conn;
int err; int err;
hci_dev_lock(hdev);
memset(&rp, 0, sizeof(rp)); memset(&rp, 0, sizeof(rp));
bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
rp.addr.type = cp->addr.type; 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)) { if (!hdev_is_powered(hdev)) {
err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
@ -1596,6 +1654,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len) u16 len)
{ {
struct mgmt_cp_disconnect *cp = data; struct mgmt_cp_disconnect *cp = data;
struct mgmt_rp_disconnect rp;
struct hci_cp_disconnect dc; struct hci_cp_disconnect dc;
struct pending_cmd *cmd; struct pending_cmd *cmd;
struct hci_conn *conn; struct hci_conn *conn;
@ -1603,17 +1662,26 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG(""); 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); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
MGMT_STATUS_NOT_POWERED); MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
goto failed; goto failed;
} }
if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
MGMT_STATUS_BUSY); MGMT_STATUS_BUSY, &rp, sizeof(rp));
goto failed; goto failed;
} }
@ -1624,8 +1692,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); conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) { if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
MGMT_STATUS_NOT_CONNECTED); MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
goto failed; goto failed;
} }
@ -1903,11 +1971,20 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG(""); 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); hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) { if (!hdev_is_powered(hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE, err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
MGMT_STATUS_NOT_POWERED); MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
goto unlock; goto unlock;
} }
@ -1924,10 +2001,6 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr,
cp->addr.type, sec_level, auth_type); 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)) { if (IS_ERR(conn)) {
int status; int status;
@ -2254,24 +2327,16 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(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, err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
cp->randomizer); cp->randomizer);
if (err < 0) if (err < 0)
status = MGMT_STATUS_FAILED; status = MGMT_STATUS_FAILED;
else else
status = 0; status = MGMT_STATUS_SUCCESS;
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
&cp->addr, sizeof(cp->addr)); &cp->addr, sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
return err; return err;
} }
@ -2287,24 +2352,15 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(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); err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
if (err < 0) if (err < 0)
status = MGMT_STATUS_INVALID_PARAMS; status = MGMT_STATUS_INVALID_PARAMS;
else else
status = 0; status = MGMT_STATUS_SUCCESS;
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
status, &cp->addr, sizeof(cp->addr)); status, &cp->addr, sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
return err; return err;
} }
@ -2365,31 +2421,45 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
switch (hdev->discovery.type) { switch (hdev->discovery.type) {
case DISCOV_TYPE_BREDR: case DISCOV_TYPE_BREDR:
if (lmp_bredr_capable(hdev)) 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); err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
else
err = -ENOTSUPP;
break; break;
case DISCOV_TYPE_LE: case DISCOV_TYPE_LE:
if (lmp_host_le_capable(hdev)) 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, err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
else
err = -ENOTSUPP;
break; break;
case DISCOV_TYPE_INTERLEAVED: case DISCOV_TYPE_INTERLEAVED:
if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev)) if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) {
err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
LE_SCAN_WIN, 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); LE_SCAN_TIMEOUT_BREDR_LE);
else
err = -ENOTSUPP;
break; break;
default: 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) if (err < 0)
@ -2510,7 +2580,8 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
hci_inquiry_cache_update_resolve(hdev, e); 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: failed:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
@ -2526,13 +2597,18 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("%s", hdev->name); 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); hci_dev_lock(hdev);
err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
if (err < 0) if (err < 0)
status = MGMT_STATUS_FAILED; status = MGMT_STATUS_FAILED;
else else
status = 0; status = MGMT_STATUS_SUCCESS;
err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
&cp->addr, sizeof(cp->addr)); &cp->addr, sizeof(cp->addr));
@ -2551,13 +2627,18 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("%s", hdev->name); 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); hci_dev_lock(hdev);
err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
if (err < 0) if (err < 0)
status = MGMT_STATUS_INVALID_PARAMS; status = MGMT_STATUS_INVALID_PARAMS;
else else
status = 0; status = MGMT_STATUS_SUCCESS;
err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
&cp->addr, sizeof(cp->addr)); &cp->addr, sizeof(cp->addr));
@ -2612,6 +2693,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_NOT_SUPPORTED); 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)) if (!hdev_is_powered(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_NOT_POWERED); MGMT_STATUS_NOT_POWERED);
@ -2659,12 +2744,23 @@ done:
return err; 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, static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
void *cp_data, u16 len) void *cp_data, u16 len)
{ {
struct mgmt_cp_load_long_term_keys *cp = cp_data; struct mgmt_cp_load_long_term_keys *cp = cp_data;
u16 key_count, expected_len; u16 key_count, expected_len;
int i; int i, err;
key_count = __le16_to_cpu(cp->key_count); key_count = __le16_to_cpu(cp->key_count);
@ -2674,11 +2770,20 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
BT_ERR("load_keys: expected %u bytes, got %u bytes", BT_ERR("load_keys: expected %u bytes, got %u bytes",
len, expected_len); len, expected_len);
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 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); 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_dev_lock(hdev);
hci_smp_ltks_clear(hdev); hci_smp_ltks_clear(hdev);
@ -2698,9 +2803,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
key->enc_size, key->ediv, key->rand); 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); hci_dev_unlock(hdev);
return 0; return err;
} }
static const struct mgmt_handler { static const struct mgmt_handler {
@ -2946,7 +3054,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
} }
} else { } else {
u8 status = MGMT_STATUS_NOT_POWERED; u8 status = MGMT_STATUS_NOT_POWERED;
u8 zero_cod[] = { 0, 0, 0 };
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); 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); err = new_settings(hdev, match.sk);