Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem
Conflicts: drivers/net/wireless/rtlwifi/pci.c include/linux/netlink.h
This commit is contained in:
@@ -22,6 +22,7 @@ menuconfig BT
|
||||
BNEP Module (Bluetooth Network Encapsulation Protocol)
|
||||
CMTP Module (CAPI Message Transport Protocol)
|
||||
HIDP Module (Human Interface Device Protocol)
|
||||
SMP Module (Security Manager Protocol)
|
||||
|
||||
Say Y here to compile Bluetooth support into the kernel or say M to
|
||||
compile it as module (bluetooth).
|
||||
@@ -36,11 +37,18 @@ if BT != n
|
||||
config BT_L2CAP
|
||||
bool "L2CAP protocol support"
|
||||
select CRC16
|
||||
select CRYPTO
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_ECB
|
||||
help
|
||||
L2CAP (Logical Link Control and Adaptation Protocol) provides
|
||||
connection oriented and connection-less data transport. L2CAP
|
||||
support is required for most Bluetooth applications.
|
||||
|
||||
Also included is support for SMP (Security Manager Protocol) which
|
||||
is the security layer on top of LE (Low Energy) links.
|
||||
|
||||
config BT_SCO
|
||||
bool "SCO links support"
|
||||
help
|
||||
|
@@ -9,5 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
|
||||
obj-$(CONFIG_BT_HIDP) += hidp/
|
||||
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o
|
||||
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o
|
||||
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o smp.o
|
||||
bluetooth-$(CONFIG_BT_SCO) += sco.o
|
||||
|
@@ -326,7 +326,7 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
|
||||
{
|
||||
struct capi_ctr *ctrl = &session->ctrl;
|
||||
struct cmtp_application *application;
|
||||
__u16 cmd, appl;
|
||||
__u16 appl;
|
||||
__u32 contr;
|
||||
|
||||
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
|
||||
@@ -344,7 +344,6 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
|
||||
appl = CAPIMSG_APPID(skb->data);
|
||||
contr = CAPIMSG_CONTROL(skb->data);
|
||||
|
||||
|
@@ -53,11 +53,13 @@ static void hci_le_connect(struct hci_conn *conn)
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = 1;
|
||||
conn->link_mode |= HCI_LM_MASTER;
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.scan_interval = cpu_to_le16(0x0004);
|
||||
cp.scan_window = cpu_to_le16(0x0004);
|
||||
bacpy(&cp.peer_addr, &conn->dst);
|
||||
cp.peer_addr_type = conn->dst_type;
|
||||
cp.conn_interval_min = cpu_to_le16(0x0008);
|
||||
cp.conn_interval_max = cpu_to_le16(0x0100);
|
||||
cp.supervision_timeout = cpu_to_le16(0x0064);
|
||||
@@ -203,6 +205,55 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
||||
}
|
||||
EXPORT_SYMBOL(hci_le_conn_update);
|
||||
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
||||
__u8 ltk[16])
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_start_enc cp;
|
||||
|
||||
BT_DBG("%p", conn);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
memcpy(cp.ltk, ltk, sizeof(cp.ltk));
|
||||
cp.ediv = ediv;
|
||||
memcpy(cp.rand, rand, sizeof(rand));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_le_start_enc);
|
||||
|
||||
void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16])
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_ltk_reply cp;
|
||||
|
||||
BT_DBG("%p", conn);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
memcpy(cp.ltk, ltk, sizeof(ltk));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_le_ltk_reply);
|
||||
|
||||
void hci_le_ltk_neg_reply(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_ltk_neg_reply cp;
|
||||
|
||||
BT_DBG("%p", conn);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
/* Device _must_ be locked */
|
||||
void hci_sco_setup(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
@@ -447,14 +498,23 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
|
||||
BT_DBG("%s dst %s", hdev->name, batostr(dst));
|
||||
|
||||
if (type == LE_LINK) {
|
||||
struct adv_entry *entry;
|
||||
|
||||
le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
|
||||
if (le)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
entry = hci_find_adv_entry(hdev, dst);
|
||||
if (!entry)
|
||||
return ERR_PTR(-EHOSTUNREACH);
|
||||
|
||||
le = hci_conn_add(hdev, LE_LINK, dst);
|
||||
if (!le)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (le->state == BT_OPEN)
|
||||
hci_le_connect(le);
|
||||
|
||||
le->dst_type = entry->bdaddr_type;
|
||||
|
||||
hci_le_connect(le);
|
||||
|
||||
hci_conn_hold(le);
|
||||
|
||||
@@ -497,7 +557,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
|
||||
if (acl->state == BT_CONNECTED &&
|
||||
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
|
||||
acl->power_save = 1;
|
||||
hci_conn_enter_active_mode(acl);
|
||||
hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON);
|
||||
|
||||
if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) {
|
||||
/* defer SCO setup until mode change completed */
|
||||
@@ -548,6 +608,8 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
|
||||
sizeof(cp), &cp);
|
||||
if (conn->key_type != 0xff)
|
||||
set_bit(HCI_CONN_REAUTH_PEND, &conn->pend);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -608,11 +670,11 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
||||
goto encrypt;
|
||||
|
||||
auth:
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
return 0;
|
||||
|
||||
hci_conn_auth(conn, sec_level, auth_type);
|
||||
return 0;
|
||||
if (!hci_conn_auth(conn, sec_level, auth_type))
|
||||
return 0;
|
||||
|
||||
encrypt:
|
||||
if (conn->link_mode & HCI_LM_ENCRYPT)
|
||||
@@ -631,9 +693,7 @@ int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level)
|
||||
if (sec_level != BT_SECURITY_HIGH)
|
||||
return 1; /* Accept if non-secure is required */
|
||||
|
||||
if (conn->key_type == HCI_LK_AUTH_COMBINATION ||
|
||||
(conn->key_type == HCI_LK_COMBINATION &&
|
||||
conn->pin_length == 16))
|
||||
if (conn->sec_level == BT_SECURITY_HIGH)
|
||||
return 1;
|
||||
|
||||
return 0; /* Reject not secure link */
|
||||
@@ -676,7 +736,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
|
||||
EXPORT_SYMBOL(hci_conn_switch_role);
|
||||
|
||||
/* Enter active mode */
|
||||
void hci_conn_enter_active_mode(struct hci_conn *conn)
|
||||
void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
@@ -685,7 +745,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn)
|
||||
if (test_bit(HCI_RAW, &hdev->flags))
|
||||
return;
|
||||
|
||||
if (conn->mode != HCI_CM_SNIFF || !conn->power_save)
|
||||
if (conn->mode != HCI_CM_SNIFF)
|
||||
goto timer;
|
||||
|
||||
if (!conn->power_save && !force_active)
|
||||
goto timer;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
|
||||
|
@@ -42,6 +42,7 @@
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
@@ -59,6 +60,8 @@ static void hci_tx_task(unsigned long arg);
|
||||
|
||||
static DEFINE_RWLOCK(hci_task_lock);
|
||||
|
||||
static int enable_smp;
|
||||
|
||||
/* HCI device list */
|
||||
LIST_HEAD(hci_dev_list);
|
||||
DEFINE_RWLOCK(hci_dev_list_lock);
|
||||
@@ -1202,6 +1205,177 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
|
||||
if (bacmp(bdaddr, &b->bdaddr) == 0)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hci_blacklist_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
|
||||
list_del(p);
|
||||
kfree(b);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct bdaddr_list *entry;
|
||||
int err;
|
||||
|
||||
if (bacmp(bdaddr, BDADDR_ANY) == 0)
|
||||
return -EBADF;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (hci_blacklist_lookup(hdev, bdaddr)) {
|
||||
err = -EEXIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
return -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
bacpy(&entry->bdaddr, bdaddr);
|
||||
|
||||
list_add(&entry->list, &hdev->blacklist);
|
||||
|
||||
err = 0;
|
||||
|
||||
err:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct bdaddr_list *entry;
|
||||
int err = 0;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
|
||||
hci_blacklist_clear(hdev);
|
||||
goto done;
|
||||
}
|
||||
|
||||
entry = hci_blacklist_lookup(hdev, bdaddr);
|
||||
if (!entry) {
|
||||
err = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hci_clear_adv_cache(unsigned long arg)
|
||||
{
|
||||
struct hci_dev *hdev = (void *) arg;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hci_adv_entries_clear(hdev);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
int hci_adv_entries_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct adv_entry *entry, *tmp;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &hdev->adv_entries, list) {
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
BT_DBG("%s adv cache cleared", hdev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct adv_entry *hci_find_adv_entry(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct adv_entry *entry;
|
||||
|
||||
list_for_each_entry(entry, &hdev->adv_entries, list)
|
||||
if (bacmp(bdaddr, &entry->bdaddr) == 0)
|
||||
return entry;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int is_connectable_adv(u8 evt_type)
|
||||
{
|
||||
if (evt_type == ADV_IND || evt_type == ADV_DIRECT_IND)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_add_adv_entry(struct hci_dev *hdev,
|
||||
struct hci_ev_le_advertising_info *ev)
|
||||
{
|
||||
struct adv_entry *entry;
|
||||
|
||||
if (!is_connectable_adv(ev->evt_type))
|
||||
return -EINVAL;
|
||||
|
||||
/* Only new entries should be added to adv_entries. So, if
|
||||
* bdaddr was found, don't add it. */
|
||||
if (hci_find_adv_entry(hdev, &ev->bdaddr))
|
||||
return 0;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
bacpy(&entry->bdaddr, &ev->bdaddr);
|
||||
entry->bdaddr_type = ev->bdaddr_type;
|
||||
|
||||
list_add(&entry->list, &hdev->adv_entries);
|
||||
|
||||
BT_DBG("%s adv entry added: address %s type %u", hdev->name,
|
||||
batostr(&entry->bdaddr), entry->bdaddr_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct crypto_blkcipher *alloc_cypher(void)
|
||||
{
|
||||
if (enable_smp)
|
||||
return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
/* Register HCI device */
|
||||
int hci_register_dev(struct hci_dev *hdev)
|
||||
{
|
||||
@@ -1268,6 +1442,10 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
|
||||
INIT_LIST_HEAD(&hdev->remote_oob_data);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->adv_entries);
|
||||
setup_timer(&hdev->adv_timer, hci_clear_adv_cache,
|
||||
(unsigned long) hdev);
|
||||
|
||||
INIT_WORK(&hdev->power_on, hci_power_on);
|
||||
INIT_WORK(&hdev->power_off, hci_power_off);
|
||||
setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
|
||||
@@ -1282,6 +1460,11 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
if (!hdev->workqueue)
|
||||
goto nomem;
|
||||
|
||||
hdev->tfm = alloc_cypher();
|
||||
if (IS_ERR(hdev->tfm))
|
||||
BT_INFO("Failed to load transform for ecb(aes): %ld",
|
||||
PTR_ERR(hdev->tfm));
|
||||
|
||||
hci_register_sysfs(hdev);
|
||||
|
||||
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
|
||||
@@ -1330,6 +1513,9 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
||||
!test_bit(HCI_SETUP, &hdev->flags))
|
||||
mgmt_index_removed(hdev->id);
|
||||
|
||||
if (!IS_ERR(hdev->tfm))
|
||||
crypto_free_blkcipher(hdev->tfm);
|
||||
|
||||
hci_notify(hdev, HCI_DEV_UNREG);
|
||||
|
||||
if (hdev->rfkill) {
|
||||
@@ -1340,6 +1526,7 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
||||
hci_unregister_sysfs(hdev);
|
||||
|
||||
hci_del_off_timer(hdev);
|
||||
del_timer(&hdev->adv_timer);
|
||||
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
|
||||
@@ -1348,6 +1535,7 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
||||
hci_uuids_clear(hdev);
|
||||
hci_link_keys_clear(hdev);
|
||||
hci_remote_oob_data_clear(hdev);
|
||||
hci_adv_entries_clear(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
__hci_dev_put(hdev);
|
||||
@@ -1891,7 +2079,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
|
||||
hci_conn_enter_active_mode(conn);
|
||||
hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active);
|
||||
|
||||
hci_send_frame(skb);
|
||||
hdev->acl_last_tx = jiffies;
|
||||
@@ -2030,7 +2218,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
if (conn) {
|
||||
register struct hci_proto *hp;
|
||||
|
||||
hci_conn_enter_active_mode(conn);
|
||||
hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active);
|
||||
|
||||
/* Send to upper protocol */
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
@@ -2164,3 +2352,6 @@ static void hci_cmd_task(unsigned long arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module_param(enable_smp, bool, 0644);
|
||||
MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)");
|
||||
|
@@ -841,6 +841,57 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
|
||||
rp->randomizer, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_cp_le_set_scan_enable *cp;
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (cp->enable == 0x01) {
|
||||
del_timer(&hdev->adv_timer);
|
||||
hci_adv_entries_clear(hdev);
|
||||
} else if (cp->enable == 0x00) {
|
||||
mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
|
||||
}
|
||||
|
||||
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
@@ -1209,16 +1260,23 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
|
||||
} else {
|
||||
if (!conn) {
|
||||
conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
|
||||
if (conn)
|
||||
if (conn) {
|
||||
conn->dst_type = cp->peer_addr_type;
|
||||
conn->out = 1;
|
||||
else
|
||||
} else {
|
||||
BT_ERR("No memory for new connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
}
|
||||
|
||||
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
@@ -1462,51 +1520,58 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (conn) {
|
||||
if (!ev->status) {
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
if (!ev->status) {
|
||||
if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) &&
|
||||
test_bit(HCI_CONN_REAUTH_PEND, &conn->pend)) {
|
||||
BT_INFO("re-auth of legacy device is not possible.");
|
||||
} else {
|
||||
conn->link_mode |= HCI_LM_AUTH;
|
||||
conn->sec_level = conn->pending_sec_level;
|
||||
} else {
|
||||
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
|
||||
}
|
||||
} else {
|
||||
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
|
||||
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
|
||||
clear_bit(HCI_CONN_REAUTH_PEND, &conn->pend);
|
||||
|
||||
if (conn->state == BT_CONFIG) {
|
||||
if (!ev->status && hdev->ssp_mode > 0 &&
|
||||
conn->ssp_mode > 0) {
|
||||
struct hci_cp_set_conn_encrypt cp;
|
||||
cp.handle = ev->handle;
|
||||
cp.encrypt = 0x01;
|
||||
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT,
|
||||
sizeof(cp), &cp);
|
||||
} else {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
if (conn->state == BT_CONFIG) {
|
||||
if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0) {
|
||||
struct hci_cp_set_conn_encrypt cp;
|
||||
cp.handle = ev->handle;
|
||||
cp.encrypt = 0x01;
|
||||
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
|
||||
&cp);
|
||||
} else {
|
||||
hci_auth_cfm(conn, ev->status);
|
||||
|
||||
hci_conn_hold(conn);
|
||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
} else {
|
||||
hci_auth_cfm(conn, ev->status);
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
|
||||
if (!ev->status) {
|
||||
struct hci_cp_set_conn_encrypt cp;
|
||||
cp.handle = ev->handle;
|
||||
cp.encrypt = 0x01;
|
||||
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT,
|
||||
sizeof(cp), &cp);
|
||||
} else {
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
|
||||
hci_encrypt_cfm(conn, ev->status, 0x00);
|
||||
}
|
||||
hci_conn_hold(conn);
|
||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
|
||||
if (!ev->status) {
|
||||
struct hci_cp_set_conn_encrypt cp;
|
||||
cp.handle = ev->handle;
|
||||
cp.encrypt = 0x01;
|
||||
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
|
||||
&cp);
|
||||
} else {
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
|
||||
hci_encrypt_cfm(conn, ev->status, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@@ -1557,6 +1622,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
|
||||
/* Encryption implies authentication */
|
||||
conn->link_mode |= HCI_LM_AUTH;
|
||||
conn->link_mode |= HCI_LM_ENCRYPT;
|
||||
conn->sec_level = conn->pending_sec_level;
|
||||
} else
|
||||
conn->link_mode &= ~HCI_LM_ENCRYPT;
|
||||
}
|
||||
@@ -1816,6 +1882,18 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
|
||||
hci_cc_user_confirm_neg_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_SET_SCAN_ENABLE:
|
||||
hci_cc_le_set_scan_enable(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_LTK_REPLY:
|
||||
hci_cc_le_ltk_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_LTK_NEG_REPLY:
|
||||
hci_cc_le_ltk_neg_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
@@ -1894,6 +1972,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_cs_le_create_conn(hdev, ev->status);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_START_ENC:
|
||||
hci_cs_le_start_enc(hdev, ev->status);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
@@ -2658,6 +2740,8 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
||||
hci_dev_unlock(hdev);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->dst_type = ev->bdaddr_type;
|
||||
}
|
||||
|
||||
if (ev->status) {
|
||||
@@ -2670,6 +2754,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
||||
|
||||
mgmt_connected(hdev->id, &ev->bdaddr);
|
||||
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
@@ -2682,6 +2767,49 @@ unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_advertising_info *ev;
|
||||
u8 num_reports;
|
||||
|
||||
num_reports = skb->data[0];
|
||||
ev = (void *) &skb->data[1];
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hci_add_adv_entry(hdev, ev);
|
||||
|
||||
while (--num_reports) {
|
||||
ev = (void *) (ev->data + ev->length + 1);
|
||||
hci_add_adv_entry(hdev, ev);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_ltk_req *ev = (void *) skb->data;
|
||||
struct hci_cp_le_ltk_reply cp;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
||||
@@ -2693,6 +2821,14 @@ static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_le_conn_complete_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_LE_ADVERTISING_REPORT:
|
||||
hci_le_adv_report_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_LE_LTK_REQ:
|
||||
hci_le_ltk_request_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@@ -180,82 +180,24 @@ static int hci_sock_release(struct socket *sock)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
|
||||
if (bacmp(bdaddr, &b->bdaddr) == 0)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg)
|
||||
static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
|
||||
{
|
||||
bdaddr_t bdaddr;
|
||||
struct bdaddr_list *entry;
|
||||
|
||||
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
||||
return -EFAULT;
|
||||
|
||||
if (bacmp(&bdaddr, BDADDR_ANY) == 0)
|
||||
return -EBADF;
|
||||
|
||||
if (hci_blacklist_lookup(hdev, &bdaddr))
|
||||
return -EEXIST;
|
||||
|
||||
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
bacpy(&entry->bdaddr, &bdaddr);
|
||||
|
||||
list_add(&entry->list, &hdev->blacklist);
|
||||
|
||||
return 0;
|
||||
return hci_blacklist_add(hdev, &bdaddr);
|
||||
}
|
||||
|
||||
int hci_blacklist_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
|
||||
list_del(p);
|
||||
kfree(b);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg)
|
||||
static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
|
||||
{
|
||||
bdaddr_t bdaddr;
|
||||
struct bdaddr_list *entry;
|
||||
|
||||
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
||||
return -EFAULT;
|
||||
|
||||
if (bacmp(&bdaddr, BDADDR_ANY) == 0)
|
||||
return hci_blacklist_clear(hdev);
|
||||
|
||||
entry = hci_blacklist_lookup(hdev, &bdaddr);
|
||||
if (!entry)
|
||||
return -ENOENT;
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
|
||||
return 0;
|
||||
return hci_blacklist_del(hdev, &bdaddr);
|
||||
}
|
||||
|
||||
/* Ioctls that require bound socket */
|
||||
@@ -290,12 +232,12 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign
|
||||
case HCIBLOCKADDR:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EACCES;
|
||||
return hci_blacklist_add(hdev, (void __user *) arg);
|
||||
return hci_sock_blacklist_add(hdev, (void __user *) arg);
|
||||
|
||||
case HCIUNBLOCKADDR:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EACCES;
|
||||
return hci_blacklist_del(hdev, (void __user *) arg);
|
||||
return hci_sock_blacklist_del(hdev, (void __user *) arg);
|
||||
|
||||
default:
|
||||
if (hdev->ioctl)
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -29,54 +29,11 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/smp.h>
|
||||
|
||||
static const struct proto_ops l2cap_sock_ops;
|
||||
|
||||
/* ---- L2CAP timers ---- */
|
||||
static void l2cap_sock_timeout(unsigned long arg)
|
||||
{
|
||||
struct sock *sk = (struct sock *) arg;
|
||||
int reason;
|
||||
|
||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if (sock_owned_by_user(sk)) {
|
||||
/* sk is owned by user. Try again later */
|
||||
l2cap_sock_set_timer(sk, HZ / 5);
|
||||
bh_unlock_sock(sk);
|
||||
sock_put(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
|
||||
reason = ECONNREFUSED;
|
||||
else if (sk->sk_state == BT_CONNECT &&
|
||||
l2cap_pi(sk)->chan->sec_level != BT_SECURITY_SDP)
|
||||
reason = ECONNREFUSED;
|
||||
else
|
||||
reason = ETIMEDOUT;
|
||||
|
||||
__l2cap_sock_close(sk, reason);
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
|
||||
l2cap_sock_kill(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
void l2cap_sock_set_timer(struct sock *sk, long timeout)
|
||||
{
|
||||
BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout);
|
||||
sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout);
|
||||
}
|
||||
|
||||
void l2cap_sock_clear_timer(struct sock *sk)
|
||||
{
|
||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||
sk_stop_timer(sk, &sk->sk_timer);
|
||||
}
|
||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio);
|
||||
|
||||
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||
{
|
||||
@@ -133,6 +90,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||
chan->sec_level = BT_SECURITY_SDP;
|
||||
|
||||
bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
|
||||
|
||||
chan->state = BT_BOUND;
|
||||
sk->sk_state = BT_BOUND;
|
||||
|
||||
done:
|
||||
@@ -162,7 +121,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM)
|
||||
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED
|
||||
&& !(la.l2_psm || la.l2_cid)) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
@@ -204,8 +163,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
|
||||
}
|
||||
|
||||
/* PSM must be odd and lsb of upper byte must be 0 */
|
||||
if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 &&
|
||||
sk->sk_type != SOCK_RAW && !la.l2_cid) {
|
||||
if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && !la.l2_cid &&
|
||||
chan->chan_type != L2CAP_CHAN_RAW) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
@@ -258,6 +217,8 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
||||
|
||||
sk->sk_max_ack_backlog = backlog;
|
||||
sk->sk_ack_backlog = 0;
|
||||
|
||||
chan->state = BT_LISTEN;
|
||||
sk->sk_state = BT_LISTEN;
|
||||
|
||||
done:
|
||||
@@ -437,6 +398,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
struct bt_security sec;
|
||||
struct bt_power pwr;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
@@ -454,8 +416,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
||||
|
||||
switch (optname) {
|
||||
case BT_SECURITY:
|
||||
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
|
||||
&& sk->sk_type != SOCK_RAW) {
|
||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
||||
chan->chan_type != L2CAP_CHAN_RAW) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
@@ -485,6 +447,21 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
||||
|
||||
break;
|
||||
|
||||
case BT_POWER:
|
||||
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
|
||||
&& sk->sk_type != SOCK_RAW) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
pwr.force_active = chan->force_active;
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(pwr));
|
||||
if (copy_to_user(optval, (char *) &pwr, len))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
@@ -535,7 +512,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
||||
chan->mode = opts.mode;
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE;
|
||||
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||
break;
|
||||
case L2CAP_MODE_ERTM:
|
||||
case L2CAP_MODE_STREAMING:
|
||||
@@ -585,6 +562,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
struct bt_security sec;
|
||||
struct bt_power pwr;
|
||||
struct l2cap_conn *conn;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
@@ -600,8 +579,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
||||
|
||||
switch (optname) {
|
||||
case BT_SECURITY:
|
||||
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
|
||||
&& sk->sk_type != SOCK_RAW) {
|
||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
||||
chan->chan_type != L2CAP_CHAN_RAW) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
@@ -621,6 +600,20 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
||||
}
|
||||
|
||||
chan->sec_level = sec.level;
|
||||
|
||||
conn = chan->conn;
|
||||
if (conn && chan->scid == L2CAP_CID_LE_DATA) {
|
||||
if (!conn->hcon->out) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (smp_conn_security(conn, sec.level))
|
||||
break;
|
||||
|
||||
err = 0;
|
||||
sk->sk_state = BT_CONFIG;
|
||||
}
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
@@ -661,6 +654,23 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
||||
chan->flushable = opt;
|
||||
break;
|
||||
|
||||
case BT_POWER:
|
||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
||||
chan->chan_type != L2CAP_CHAN_RAW) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
pwr.force_active = BT_POWER_FORCE_ACTIVE_ON;
|
||||
|
||||
len = min_t(unsigned int, sizeof(pwr), optlen);
|
||||
if (copy_from_user((char *) &pwr, optval, len)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
chan->force_active = pwr.force_active;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
@@ -674,8 +684,6 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
struct sk_buff *skb;
|
||||
u16 control;
|
||||
int err;
|
||||
|
||||
BT_DBG("sock %p, sk %p", sock, sk);
|
||||
@@ -690,87 +698,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state != BT_CONNECTED) {
|
||||
err = -ENOTCONN;
|
||||
goto done;
|
||||
release_sock(sk);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
/* Connectionless channel */
|
||||
if (sk->sk_type == SOCK_DGRAM) {
|
||||
skb = l2cap_create_connless_pdu(chan, msg, len);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
} else {
|
||||
l2cap_do_send(chan, skb);
|
||||
err = len;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
err = l2cap_chan_send(chan, msg, len);
|
||||
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
/* Check outgoing MTU */
|
||||
if (len > chan->omtu) {
|
||||
err = -EMSGSIZE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Create a basic PDU */
|
||||
skb = l2cap_create_basic_pdu(chan, msg, len);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
goto done;
|
||||
}
|
||||
|
||||
l2cap_do_send(chan, skb);
|
||||
err = len;
|
||||
break;
|
||||
|
||||
case L2CAP_MODE_ERTM:
|
||||
case L2CAP_MODE_STREAMING:
|
||||
/* Entire SDU fits into one PDU */
|
||||
if (len <= chan->remote_mps) {
|
||||
control = L2CAP_SDU_UNSEGMENTED;
|
||||
skb = l2cap_create_iframe_pdu(chan, msg, len, control,
|
||||
0);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
goto done;
|
||||
}
|
||||
__skb_queue_tail(&chan->tx_q, skb);
|
||||
|
||||
if (chan->tx_send_head == NULL)
|
||||
chan->tx_send_head = skb;
|
||||
|
||||
} else {
|
||||
/* Segment SDU into multiples PDUs */
|
||||
err = l2cap_sar_segment_sdu(chan, msg, len);
|
||||
if (err < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (chan->mode == L2CAP_MODE_STREAMING) {
|
||||
l2cap_streaming_send(chan);
|
||||
err = len;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
|
||||
(chan->conn_state & L2CAP_CONN_WAIT_F)) {
|
||||
err = len;
|
||||
break;
|
||||
}
|
||||
err = l2cap_ertm_send(chan);
|
||||
|
||||
if (err >= 0)
|
||||
err = len;
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("bad state %1.1x", chan->mode);
|
||||
err = -EBADFD;
|
||||
}
|
||||
|
||||
done:
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
@@ -800,7 +733,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
||||
/* Kill socket (only if zapped and orphan)
|
||||
* Must be called on unlocked socket.
|
||||
*/
|
||||
void l2cap_sock_kill(struct sock *sk)
|
||||
static void l2cap_sock_kill(struct sock *sk)
|
||||
{
|
||||
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
|
||||
return;
|
||||
@@ -814,87 +747,6 @@ void l2cap_sock_kill(struct sock *sk)
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
/* Must be called on unlocked socket. */
|
||||
static void l2cap_sock_close(struct sock *sk)
|
||||
{
|
||||
l2cap_sock_clear_timer(sk);
|
||||
lock_sock(sk);
|
||||
__l2cap_sock_close(sk, ECONNRESET);
|
||||
release_sock(sk);
|
||||
l2cap_sock_kill(sk);
|
||||
}
|
||||
|
||||
static void l2cap_sock_cleanup_listen(struct sock *parent)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
BT_DBG("parent %p", parent);
|
||||
|
||||
/* Close not yet accepted channels */
|
||||
while ((sk = bt_accept_dequeue(parent, NULL)))
|
||||
l2cap_sock_close(sk);
|
||||
|
||||
parent->sk_state = BT_CLOSED;
|
||||
sock_set_flag(parent, SOCK_ZAPPED);
|
||||
}
|
||||
|
||||
void __l2cap_sock_close(struct sock *sk, int reason)
|
||||
{
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
|
||||
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
|
||||
|
||||
switch (sk->sk_state) {
|
||||
case BT_LISTEN:
|
||||
l2cap_sock_cleanup_listen(sk);
|
||||
break;
|
||||
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
if ((sk->sk_type == SOCK_SEQPACKET ||
|
||||
sk->sk_type == SOCK_STREAM) &&
|
||||
conn->hcon->type == ACL_LINK) {
|
||||
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||
l2cap_send_disconn_req(conn, chan, reason);
|
||||
} else
|
||||
l2cap_chan_del(chan, reason);
|
||||
break;
|
||||
|
||||
case BT_CONNECT2:
|
||||
if ((sk->sk_type == SOCK_SEQPACKET ||
|
||||
sk->sk_type == SOCK_STREAM) &&
|
||||
conn->hcon->type == ACL_LINK) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 result;
|
||||
|
||||
if (bt_sk(sk)->defer_setup)
|
||||
result = L2CAP_CR_SEC_BLOCK;
|
||||
else
|
||||
result = L2CAP_CR_BAD_PSM;
|
||||
|
||||
rsp.scid = cpu_to_le16(chan->dcid);
|
||||
rsp.dcid = cpu_to_le16(chan->scid);
|
||||
rsp.result = cpu_to_le16(result);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
|
||||
sizeof(rsp), &rsp);
|
||||
}
|
||||
|
||||
l2cap_chan_del(chan, reason);
|
||||
break;
|
||||
|
||||
case BT_CONNECT:
|
||||
case BT_DISCONN:
|
||||
l2cap_chan_del(chan, reason);
|
||||
break;
|
||||
|
||||
default:
|
||||
sock_set_flag(sk, SOCK_ZAPPED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int l2cap_sock_shutdown(struct socket *sock, int how)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
@@ -912,8 +764,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
||||
err = __l2cap_wait_ack(sk);
|
||||
|
||||
sk->sk_shutdown = SHUTDOWN_MASK;
|
||||
l2cap_sock_clear_timer(sk);
|
||||
__l2cap_sock_close(sk, 0);
|
||||
l2cap_chan_close(chan, 0);
|
||||
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
|
||||
err = bt_sock_wait_state(sk, BT_CLOSED,
|
||||
@@ -944,6 +795,49 @@ static int l2cap_sock_release(struct socket *sock)
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data)
|
||||
{
|
||||
struct sock *sk, *parent = data;
|
||||
|
||||
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
||||
GFP_ATOMIC);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
l2cap_sock_init(sk, parent);
|
||||
|
||||
return l2cap_pi(sk)->chan;
|
||||
}
|
||||
|
||||
static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
|
||||
{
|
||||
struct sock *sk = data;
|
||||
|
||||
return sock_queue_rcv_skb(sk, skb);
|
||||
}
|
||||
|
||||
static void l2cap_sock_close_cb(void *data)
|
||||
{
|
||||
struct sock *sk = data;
|
||||
|
||||
l2cap_sock_kill(sk);
|
||||
}
|
||||
|
||||
static void l2cap_sock_state_change_cb(void *data, int state)
|
||||
{
|
||||
struct sock *sk = data;
|
||||
|
||||
sk->sk_state = state;
|
||||
}
|
||||
|
||||
static struct l2cap_ops l2cap_chan_ops = {
|
||||
.name = "L2CAP Socket Interface",
|
||||
.new_connection = l2cap_sock_new_connection_cb,
|
||||
.recv = l2cap_sock_recv_cb,
|
||||
.close = l2cap_sock_close_cb,
|
||||
.state_change = l2cap_sock_state_change_cb,
|
||||
};
|
||||
|
||||
static void l2cap_sock_destruct(struct sock *sk)
|
||||
{
|
||||
BT_DBG("sk %p", sk);
|
||||
@@ -952,7 +846,7 @@ static void l2cap_sock_destruct(struct sock *sk)
|
||||
skb_queue_purge(&sk->sk_write_queue);
|
||||
}
|
||||
|
||||
void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
{
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
struct l2cap_chan *chan = pi->chan;
|
||||
@@ -965,6 +859,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
sk->sk_type = parent->sk_type;
|
||||
bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup;
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->conf_state = pchan->conf_state;
|
||||
@@ -976,12 +871,27 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
chan->role_switch = pchan->role_switch;
|
||||
chan->force_reliable = pchan->force_reliable;
|
||||
chan->flushable = pchan->flushable;
|
||||
chan->force_active = pchan->force_active;
|
||||
} else {
|
||||
|
||||
switch (sk->sk_type) {
|
||||
case SOCK_RAW:
|
||||
chan->chan_type = L2CAP_CHAN_RAW;
|
||||
break;
|
||||
case SOCK_DGRAM:
|
||||
chan->chan_type = L2CAP_CHAN_CONN_LESS;
|
||||
break;
|
||||
case SOCK_SEQPACKET:
|
||||
case SOCK_STREAM:
|
||||
chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
|
||||
break;
|
||||
}
|
||||
|
||||
chan->imtu = L2CAP_DEFAULT_MTU;
|
||||
chan->omtu = 0;
|
||||
if (!disable_ertm && sk->sk_type == SOCK_STREAM) {
|
||||
chan->mode = L2CAP_MODE_ERTM;
|
||||
chan->conf_state |= L2CAP_CONF_STATE2_DEVICE;
|
||||
set_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||
} else {
|
||||
chan->mode = L2CAP_MODE_BASIC;
|
||||
}
|
||||
@@ -992,10 +902,15 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
chan->role_switch = 0;
|
||||
chan->force_reliable = 0;
|
||||
chan->flushable = BT_FLUSHABLE_OFF;
|
||||
chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
|
||||
|
||||
}
|
||||
|
||||
/* Default config options */
|
||||
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
|
||||
|
||||
chan->data = sk;
|
||||
chan->ops = &l2cap_chan_ops;
|
||||
}
|
||||
|
||||
static struct proto l2cap_proto = {
|
||||
@@ -1004,9 +919,10 @@ static struct proto l2cap_proto = {
|
||||
.obj_size = sizeof(struct l2cap_pinfo)
|
||||
};
|
||||
|
||||
struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);
|
||||
if (!sk)
|
||||
@@ -1023,7 +939,13 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g
|
||||
sk->sk_protocol = proto;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);
|
||||
chan = l2cap_chan_create(sk);
|
||||
if (!chan) {
|
||||
l2cap_sock_kill(sk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l2cap_pi(sk)->chan = chan;
|
||||
|
||||
return sk;
|
||||
}
|
||||
@@ -1032,7 +954,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
BT_DBG("sock %p", sock);
|
||||
|
||||
@@ -1051,14 +972,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
chan = l2cap_chan_create(sk);
|
||||
if (!chan) {
|
||||
l2cap_sock_kill(sk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l2cap_pi(sk)->chan = chan;
|
||||
|
||||
l2cap_sock_init(sk, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ struct pending_cmd {
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
LIST_HEAD(cmd_list);
|
||||
static LIST_HEAD(cmd_list);
|
||||
|
||||
static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
|
||||
{
|
||||
@@ -990,7 +990,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
||||
|
||||
put_unaligned_le16(conn->handle, &dc.handle);
|
||||
dc.reason = 0x13; /* Remote User Terminated Connection */
|
||||
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL);
|
||||
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
|
||||
}
|
||||
|
||||
unlock:
|
||||
@@ -1092,8 +1092,6 @@ static int get_connections(struct sock *sk, u16 index)
|
||||
|
||||
put_unaligned_le16(count, &rp->conn_count);
|
||||
|
||||
read_lock(&hci_dev_list_lock);
|
||||
|
||||
i = 0;
|
||||
list_for_each(p, &hdev->conn_hash.list) {
|
||||
struct hci_conn *c = list_entry(p, struct hci_conn, list);
|
||||
@@ -1101,8 +1099,6 @@ static int get_connections(struct sock *sk, u16 index)
|
||||
bacpy(&rp->conn[i++], &c->dst);
|
||||
}
|
||||
|
||||
read_unlock(&hci_dev_list_lock);
|
||||
|
||||
err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
|
||||
|
||||
unlock:
|
||||
@@ -1112,11 +1108,32 @@ unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int send_pin_code_neg_reply(struct sock *sk, u16 index,
|
||||
struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
|
||||
{
|
||||
struct pending_cmd *cmd;
|
||||
int err;
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, cp,
|
||||
sizeof(*cp));
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr),
|
||||
&cp->bdaddr);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
|
||||
u16 len)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct hci_conn *conn;
|
||||
struct mgmt_cp_pin_code_reply *cp;
|
||||
struct mgmt_cp_pin_code_neg_reply ncp;
|
||||
struct hci_cp_pin_code_reply reply;
|
||||
struct pending_cmd *cmd;
|
||||
int err;
|
||||
@@ -1139,6 +1156,25 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
||||
if (!conn) {
|
||||
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENOTCONN);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
|
||||
bacpy(&ncp.bdaddr, &cp->bdaddr);
|
||||
|
||||
BT_ERR("PIN code is not 16 bytes long");
|
||||
|
||||
err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
|
||||
if (err >= 0)
|
||||
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
|
||||
EINVAL);
|
||||
|
||||
goto failed;
|
||||
}
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
@@ -1147,7 +1183,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
|
||||
|
||||
bacpy(&reply.bdaddr, &cp->bdaddr);
|
||||
reply.pin_len = cp->pin_len;
|
||||
memcpy(reply.pin_code, cp->pin_code, 16);
|
||||
memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
|
||||
|
||||
err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
|
||||
if (err < 0)
|
||||
@@ -1165,7 +1201,6 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_pin_code_neg_reply *cp;
|
||||
struct pending_cmd *cmd;
|
||||
int err;
|
||||
|
||||
BT_DBG("");
|
||||
@@ -1189,17 +1224,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index,
|
||||
data, len);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr),
|
||||
&cp->bdaddr);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
err = send_pin_code_neg_reply(sk, index, hdev, cp);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
@@ -1641,6 +1666,70 @@ failed:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int block_device(struct sock *sk, u16 index, unsigned char *data,
|
||||
u16 len)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_block_device *cp;
|
||||
int err;
|
||||
|
||||
BT_DBG("hci%u", index);
|
||||
|
||||
cp = (void *) data;
|
||||
|
||||
if (len != sizeof(*cp))
|
||||
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||
EINVAL);
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||
ENODEV);
|
||||
|
||||
err = hci_blacklist_add(hdev, &cp->bdaddr);
|
||||
|
||||
if (err < 0)
|
||||
err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err);
|
||||
else
|
||||
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||
NULL, 0);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
|
||||
u16 len)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_unblock_device *cp;
|
||||
int err;
|
||||
|
||||
BT_DBG("hci%u", index);
|
||||
|
||||
cp = (void *) data;
|
||||
|
||||
if (len != sizeof(*cp))
|
||||
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||
EINVAL);
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||
ENODEV);
|
||||
|
||||
err = hci_blacklist_del(hdev, &cp->bdaddr);
|
||||
|
||||
if (err < 0)
|
||||
err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err);
|
||||
else
|
||||
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||
NULL, 0);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
||||
{
|
||||
unsigned char *buf;
|
||||
@@ -1755,6 +1844,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
||||
case MGMT_OP_STOP_DISCOVERY:
|
||||
err = stop_discovery(sk, index);
|
||||
break;
|
||||
case MGMT_OP_BLOCK_DEVICE:
|
||||
err = block_device(sk, index, buf + sizeof(*hdr), len);
|
||||
break;
|
||||
case MGMT_OP_UNBLOCK_DEVICE:
|
||||
err = unblock_device(sk, index, buf + sizeof(*hdr), len);
|
||||
break;
|
||||
default:
|
||||
BT_DBG("Unknown op %u", opcode);
|
||||
err = cmd_status(sk, index, opcode, 0x01);
|
||||
|
@@ -679,7 +679,8 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct bt_security sec;
|
||||
int len, err = 0;
|
||||
int err = 0;
|
||||
size_t len;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
@@ -741,7 +742,6 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
|
||||
static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sock *l2cap_sk;
|
||||
struct rfcomm_conninfo cinfo;
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
|
||||
int len, err = 0;
|
||||
@@ -786,8 +786,6 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk;
|
||||
|
||||
memset(&cinfo, 0, sizeof(cinfo));
|
||||
cinfo.hci_handle = conn->hcon->handle;
|
||||
memcpy(cinfo.dev_class, conn->hcon->dev_class, 3);
|
||||
|
534
net/bluetooth/smp.c
Normal file
534
net/bluetooth/smp.c
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/smp.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <crypto/b128ops.h>
|
||||
|
||||
#define SMP_TIMEOUT 30000 /* 30 seconds */
|
||||
|
||||
static inline void swap128(u8 src[16], u8 dst[16])
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 16; i++)
|
||||
dst[15 - i] = src[i];
|
||||
}
|
||||
|
||||
static inline void swap56(u8 src[7], u8 dst[7])
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 7; i++)
|
||||
dst[6 - i] = src[i];
|
||||
}
|
||||
|
||||
static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
|
||||
{
|
||||
struct blkcipher_desc desc;
|
||||
struct scatterlist sg;
|
||||
int err, iv_len;
|
||||
unsigned char iv[128];
|
||||
|
||||
if (tfm == NULL) {
|
||||
BT_ERR("tfm %p", tfm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc.tfm = tfm;
|
||||
desc.flags = 0;
|
||||
|
||||
err = crypto_blkcipher_setkey(tfm, k, 16);
|
||||
if (err) {
|
||||
BT_ERR("cipher setkey failed: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
sg_init_one(&sg, r, 16);
|
||||
|
||||
iv_len = crypto_blkcipher_ivsize(tfm);
|
||||
if (iv_len) {
|
||||
memset(&iv, 0xff, iv_len);
|
||||
crypto_blkcipher_set_iv(tfm, iv, iv_len);
|
||||
}
|
||||
|
||||
err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
|
||||
if (err)
|
||||
BT_ERR("Encrypt data error %d", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
|
||||
u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
|
||||
u8 _rat, bdaddr_t *ra, u8 res[16])
|
||||
{
|
||||
u8 p1[16], p2[16];
|
||||
int err;
|
||||
|
||||
memset(p1, 0, 16);
|
||||
|
||||
/* p1 = pres || preq || _rat || _iat */
|
||||
swap56(pres, p1);
|
||||
swap56(preq, p1 + 7);
|
||||
p1[14] = _rat;
|
||||
p1[15] = _iat;
|
||||
|
||||
memset(p2, 0, 16);
|
||||
|
||||
/* p2 = padding || ia || ra */
|
||||
baswap((bdaddr_t *) (p2 + 4), ia);
|
||||
baswap((bdaddr_t *) (p2 + 10), ra);
|
||||
|
||||
/* res = r XOR p1 */
|
||||
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
|
||||
|
||||
/* res = e(k, res) */
|
||||
err = smp_e(tfm, k, res);
|
||||
if (err) {
|
||||
BT_ERR("Encrypt data error");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* res = res XOR p2 */
|
||||
u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
|
||||
|
||||
/* res = e(k, res) */
|
||||
err = smp_e(tfm, k, res);
|
||||
if (err)
|
||||
BT_ERR("Encrypt data error");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
|
||||
u8 r1[16], u8 r2[16], u8 _r[16])
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Just least significant octets from r1 and r2 are considered */
|
||||
memcpy(_r, r1 + 8, 8);
|
||||
memcpy(_r + 8, r2 + 8, 8);
|
||||
|
||||
err = smp_e(tfm, k, _r);
|
||||
if (err)
|
||||
BT_ERR("Encrypt data error");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_rand(u8 *buf)
|
||||
{
|
||||
get_random_bytes(buf, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
|
||||
u16 dlen, void *data)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct l2cap_hdr *lh;
|
||||
int len;
|
||||
|
||||
len = L2CAP_HDR_SIZE + sizeof(code) + dlen;
|
||||
|
||||
if (len > conn->mtu)
|
||||
return NULL;
|
||||
|
||||
skb = bt_skb_alloc(len, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
|
||||
lh->len = cpu_to_le16(sizeof(code) + dlen);
|
||||
lh->cid = cpu_to_le16(L2CAP_CID_SMP);
|
||||
|
||||
memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
|
||||
|
||||
memcpy(skb_put(skb, dlen), data, dlen);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
|
||||
{
|
||||
struct sk_buff *skb = smp_build_cmd(conn, code, len, data);
|
||||
|
||||
BT_DBG("code 0x%2.2x", code);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
hci_send_acl(conn->hcon, skb, 0);
|
||||
}
|
||||
|
||||
static __u8 seclevel_to_authreq(__u8 level)
|
||||
{
|
||||
switch (level) {
|
||||
case BT_SECURITY_HIGH:
|
||||
/* Right now we don't support bonding */
|
||||
return SMP_AUTH_MITM;
|
||||
|
||||
default:
|
||||
return SMP_AUTH_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
struct smp_cmd_pairing *cmd, __u8 authreq)
|
||||
{
|
||||
cmd->io_capability = conn->hcon->io_capability;
|
||||
cmd->oob_flag = SMP_OOB_NOT_PRESENT;
|
||||
cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
cmd->init_key_dist = 0x00;
|
||||
cmd->resp_key_dist = 0x00;
|
||||
cmd->auth_req = authreq;
|
||||
}
|
||||
|
||||
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
|
||||
{
|
||||
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
|
||||
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
conn->smp_key_size = max_key_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
|
||||
u8 key_size;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&conn->preq[1], req, sizeof(*req));
|
||||
skb_pull(skb, sizeof(*req));
|
||||
|
||||
if (req->oob_flag)
|
||||
return SMP_OOB_NOT_AVAIL;
|
||||
|
||||
/* We didn't start the pairing, so no requirements */
|
||||
build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE);
|
||||
|
||||
key_size = min(req->max_key_size, rsp.max_key_size);
|
||||
if (check_enc_key_size(conn, key_size))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
/* Just works */
|
||||
memset(conn->tk, 0, sizeof(conn->tk));
|
||||
|
||||
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||
memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
|
||||
struct smp_cmd_pairing_confirm cp;
|
||||
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
||||
int ret;
|
||||
u8 res[16], key_size;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
skb_pull(skb, sizeof(*rsp));
|
||||
|
||||
req = (void *) &conn->preq[1];
|
||||
|
||||
key_size = min(req->max_key_size, rsp->max_key_size);
|
||||
if (check_enc_key_size(conn, key_size))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
if (rsp->oob_flag)
|
||||
return SMP_OOB_NOT_AVAIL;
|
||||
|
||||
/* Just works */
|
||||
memset(conn->tk, 0, sizeof(conn->tk));
|
||||
|
||||
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||
memcpy(&conn->prsp[1], rsp, sizeof(*rsp));
|
||||
|
||||
ret = smp_rand(conn->prnd);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
|
||||
conn->src, conn->hcon->dst_type, conn->dst, res);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
swap128(res, cp.confirm_val);
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
||||
|
||||
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
||||
|
||||
memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf));
|
||||
skb_pull(skb, sizeof(conn->pcnf));
|
||||
|
||||
if (conn->hcon->out) {
|
||||
u8 random[16];
|
||||
|
||||
swap128(conn->prnd, random);
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
|
||||
random);
|
||||
} else {
|
||||
struct smp_cmd_pairing_confirm cp;
|
||||
int ret;
|
||||
u8 res[16];
|
||||
|
||||
ret = smp_rand(conn->prnd);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp,
|
||||
conn->hcon->dst_type, conn->dst,
|
||||
0, conn->src, res);
|
||||
if (ret)
|
||||
return SMP_CONFIRM_FAILED;
|
||||
|
||||
swap128(res, cp.confirm_val);
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
struct crypto_blkcipher *tfm = hcon->hdev->tfm;
|
||||
int ret;
|
||||
u8 key[16], res[16], random[16], confirm[16];
|
||||
|
||||
swap128(skb->data, random);
|
||||
skb_pull(skb, sizeof(random));
|
||||
|
||||
memset(hcon->ltk, 0, sizeof(hcon->ltk));
|
||||
|
||||
if (conn->hcon->out)
|
||||
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
|
||||
conn->src, conn->hcon->dst_type, conn->dst,
|
||||
res);
|
||||
else
|
||||
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp,
|
||||
conn->hcon->dst_type, conn->dst, 0, conn->src,
|
||||
res);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
||||
|
||||
swap128(res, confirm);
|
||||
|
||||
if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
|
||||
BT_ERR("Pairing failed (confirmation values mismatch)");
|
||||
return SMP_CONFIRM_FAILED;
|
||||
}
|
||||
|
||||
if (conn->hcon->out) {
|
||||
__le16 ediv;
|
||||
u8 rand[8];
|
||||
|
||||
smp_s1(tfm, conn->tk, random, conn->prnd, key);
|
||||
swap128(key, hcon->ltk);
|
||||
|
||||
memset(hcon->ltk + conn->smp_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||
|
||||
memset(rand, 0, sizeof(rand));
|
||||
ediv = 0;
|
||||
hci_le_start_enc(hcon, ediv, rand, hcon->ltk);
|
||||
} else {
|
||||
u8 r[16];
|
||||
|
||||
swap128(conn->prnd, r);
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
|
||||
|
||||
smp_s1(tfm, conn->tk, conn->prnd, random, key);
|
||||
swap128(key, hcon->ltk);
|
||||
|
||||
memset(hcon->ltk + conn->smp_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct smp_cmd_security_req *rp = (void *) skb->data;
|
||||
struct smp_cmd_pairing cp;
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
||||
return 0;
|
||||
|
||||
skb_pull(skb, sizeof(*rp));
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
build_pairing_cmd(conn, &cp, rp->auth_req);
|
||||
|
||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
||||
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
__u8 authreq;
|
||||
|
||||
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
|
||||
|
||||
if (IS_ERR(hcon->hdev->tfm))
|
||||
return 1;
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
||||
return 0;
|
||||
|
||||
if (sec_level == BT_SECURITY_LOW)
|
||||
return 1;
|
||||
|
||||
if (hcon->sec_level >= sec_level)
|
||||
return 1;
|
||||
|
||||
authreq = seclevel_to_authreq(sec_level);
|
||||
|
||||
if (hcon->link_mode & HCI_LM_MASTER) {
|
||||
struct smp_cmd_pairing cp;
|
||||
|
||||
build_pairing_cmd(conn, &cp, authreq);
|
||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||
} else {
|
||||
struct smp_cmd_security_req cp;
|
||||
cp.auth_req = authreq;
|
||||
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
hcon->pending_sec_level = sec_level;
|
||||
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
__u8 code = skb->data[0];
|
||||
__u8 reason;
|
||||
int err = 0;
|
||||
|
||||
if (IS_ERR(conn->hcon->hdev->tfm)) {
|
||||
err = PTR_ERR(conn->hcon->hdev->tfm);
|
||||
reason = SMP_PAIRING_NOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
|
||||
skb_pull(skb, sizeof(code));
|
||||
|
||||
switch (code) {
|
||||
case SMP_CMD_PAIRING_REQ:
|
||||
reason = smp_cmd_pairing_req(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_FAIL:
|
||||
reason = 0;
|
||||
err = -EPERM;
|
||||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_RSP:
|
||||
reason = smp_cmd_pairing_rsp(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_SECURITY_REQ:
|
||||
reason = smp_cmd_security_req(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_CONFIRM:
|
||||
reason = smp_cmd_pairing_confirm(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_RANDOM:
|
||||
reason = smp_cmd_pairing_random(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_ENCRYPT_INFO:
|
||||
case SMP_CMD_MASTER_IDENT:
|
||||
case SMP_CMD_IDENT_INFO:
|
||||
case SMP_CMD_IDENT_ADDR_INFO:
|
||||
case SMP_CMD_SIGN_INFO:
|
||||
default:
|
||||
BT_DBG("Unknown command code 0x%2.2x", code);
|
||||
|
||||
reason = SMP_CMD_NOTSUPP;
|
||||
err = -EOPNOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (reason)
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
|
||||
&reason);
|
||||
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
Reference in New Issue
Block a user