summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile1
-rw-r--r--net/bluetooth/Kconfig9
-rw-r--r--net/bluetooth/Makefile2
-rw-r--r--net/bluetooth/cmtp/capi.c3
-rw-r--r--net/bluetooth/hci_conn.c79
-rw-r--r--net/bluetooth/hci_core.c195
-rw-r--r--net/bluetooth/hci_event.c206
-rw-r--r--net/bluetooth/hci_sock.c70
-rw-r--r--net/bluetooth/l2cap_core.c868
-rw-r--r--net/bluetooth/l2cap_sock.c375
-rw-r--r--net/bluetooth/mgmt.c133
-rw-r--r--net/bluetooth/rfcomm/sock.c6
-rw-r--r--net/bluetooth/smp.c534
-rw-r--r--net/core/sock.c6
-rw-r--r--net/mac80211/aes_ccm.c37
-rw-r--r--net/mac80211/aes_ccm.h2
-rw-r--r--net/mac80211/aes_cmac.c10
-rw-r--r--net/mac80211/aes_cmac.h2
-rw-r--r--net/mac80211/agg-rx.c21
-rw-r--r--net/mac80211/cfg.c62
-rw-r--r--net/mac80211/debugfs_key.c13
-rw-r--r--net/mac80211/driver-ops.h20
-rw-r--r--net/mac80211/driver-trace.h55
-rw-r--r--net/mac80211/ht.c6
-rw-r--r--net/mac80211/ieee80211_i.h26
-rw-r--r--net/mac80211/key.c171
-rw-r--r--net/mac80211/key.h30
-rw-r--r--net/mac80211/mesh_pathtbl.c4
-rw-r--r--net/mac80211/mlme.c56
-rw-r--r--net/mac80211/pm.c51
-rw-r--r--net/mac80211/rc80211_minstrel.c9
-rw-r--r--net/mac80211/rc80211_minstrel.h12
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c7
-rw-r--r--net/mac80211/rx.c35
-rw-r--r--net/mac80211/scan.c45
-rw-r--r--net/mac80211/sta_info.h6
-rw-r--r--net/mac80211/tkip.c108
-rw-r--r--net/mac80211/tkip.h8
-rw-r--r--net/mac80211/tx.c14
-rw-r--r--net/mac80211/util.c23
-rw-r--r--net/mac80211/wme.c3
-rw-r--r--net/mac80211/wme.h5
-rw-r--r--net/mac80211/work.c2
-rw-r--r--net/mac80211/wpa.c94
-rw-r--r--net/netlink/af_netlink.c2
-rw-r--r--net/nfc/Kconfig16
-rw-r--r--net/nfc/Makefile7
-rw-r--r--net/nfc/af_nfc.c98
-rw-r--r--net/nfc/core.c468
-rw-r--r--net/nfc/netlink.c537
-rw-r--r--net/nfc/nfc.h117
-rw-r--r--net/nfc/rawsock.c354
-rw-r--r--net/wireless/mlme.c15
-rw-r--r--net/wireless/nl80211.c232
-rw-r--r--net/wireless/nl80211.h4
-rw-r--r--net/wireless/scan.c7
57 files changed, 4312 insertions, 970 deletions
diff --git a/net/Kconfig b/net/Kconfig
index 878151c772c9..a07314844238 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -322,6 +322,7 @@ source "net/rfkill/Kconfig"
source "net/9p/Kconfig"
source "net/caif/Kconfig"
source "net/ceph/Kconfig"
+source "net/nfc/Kconfig"
endif # if NET
diff --git a/net/Makefile b/net/Makefile
index a51d9465e628..acdde4950de4 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -68,3 +68,4 @@ obj-$(CONFIG_WIMAX) += wimax/
obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/
obj-$(CONFIG_CEPH_LIB) += ceph/
obj-$(CONFIG_BATMAN_ADV) += batman-adv/
+obj-$(CONFIG_NFC) += nfc/
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 6ae5ec508587..bfb3dc03c9de 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -6,6 +6,7 @@ menuconfig BT
tristate "Bluetooth subsystem support"
depends on NET && !S390
depends on RFKILL || !RFKILL
+ select CRYPTO
help
Bluetooth is low-cost, low-power, short-range wireless technology.
It was designed as a replacement for cables and other short-range
@@ -22,6 +23,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 +38,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
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index f04fe9a9d634..9b67f3d08fa4 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -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
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 744233cba244..040f67b12978 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -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);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index bcd158f40bb9..ea7f031f3b04 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -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)
{
@@ -450,14 +501,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);
@@ -500,7 +560,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 */
@@ -551,6 +611,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;
@@ -634,9 +696,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 */
@@ -679,7 +739,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;
@@ -688,7 +748,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)) {
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 815269b07f20..0029e178e52e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -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)");
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 77930aa522e3..ac2c5e89617c 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -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;
}
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 295e4a88fff8..ff02cf5e77cc 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -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;
-}
-
-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;
+ return hci_blacklist_add(hdev, &bdaddr);
}
-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)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ebff14c69078..fc219ec28711 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -54,6 +54,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/smp.h>
int disable_ertm;
@@ -62,18 +63,34 @@ static u8 l2cap_fixed_chan[8] = { 0x02, };
static struct workqueue_struct *_busy_wq;
-LIST_HEAD(chan_list);
-DEFINE_RWLOCK(chan_list_lock);
+static LIST_HEAD(chan_list);
+static DEFINE_RWLOCK(chan_list_lock);
static void l2cap_busy_work(struct work_struct *work);
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
u8 code, u8 ident, u16 dlen, void *data);
+static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
+ void *data);
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
+static void l2cap_send_disconn_req(struct l2cap_conn *conn,
+ struct l2cap_chan *chan, int err);
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
/* ---- L2CAP channels ---- */
+
+static inline void chan_hold(struct l2cap_chan *c)
+{
+ atomic_inc(&c->refcnt);
+}
+
+static inline void chan_put(struct l2cap_chan *c)
+{
+ if (atomic_dec_and_test(&c->refcnt))
+ kfree(c);
+}
+
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
{
struct l2cap_chan *c;
@@ -204,6 +221,62 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
return 0;
}
+static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout)
+{
+ BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout);
+
+ if (!mod_timer(timer, jiffies + timeout))
+ chan_hold(chan);
+}
+
+static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer)
+{
+ BT_DBG("chan %p state %d", chan, chan->state);
+
+ if (timer_pending(timer) && del_timer(timer))
+ chan_put(chan);
+}
+
+static void l2cap_state_change(struct l2cap_chan *chan, int state)
+{
+ chan->state = state;
+ chan->ops->state_change(chan->data, state);
+}
+
+static void l2cap_chan_timeout(unsigned long arg)
+{
+ struct l2cap_chan *chan = (struct l2cap_chan *) arg;
+ struct sock *sk = chan->sk;
+ int reason;
+
+ BT_DBG("chan %p state %d", chan, chan->state);
+
+ bh_lock_sock(sk);
+
+ if (sock_owned_by_user(sk)) {
+ /* sk is owned by user. Try again later */
+ __set_chan_timer(chan, HZ / 5);
+ bh_unlock_sock(sk);
+ chan_put(chan);
+ return;
+ }
+
+ if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
+ reason = ECONNREFUSED;
+ else if (chan->state == BT_CONNECT &&
+ chan->sec_level != BT_SECURITY_SDP)
+ reason = ECONNREFUSED;
+ else
+ reason = ETIMEDOUT;
+
+ l2cap_chan_close(chan, reason);
+
+ bh_unlock_sock(sk);
+
+ chan->ops->close(chan->data);
+ chan_put(chan);
+}
+
struct l2cap_chan *l2cap_chan_create(struct sock *sk)
{
struct l2cap_chan *chan;
@@ -218,6 +291,12 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
list_add(&chan->global_l, &chan_list);
write_unlock_bh(&chan_list_lock);
+ setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan);
+
+ chan->state = BT_OPEN;
+
+ atomic_set(&chan->refcnt, 1);
+
return chan;
}
@@ -227,13 +306,11 @@ void l2cap_chan_destroy(struct l2cap_chan *chan)
list_del(&chan->global_l);
write_unlock_bh(&chan_list_lock);
- kfree(chan);
+ chan_put(chan);
}
static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
- struct sock *sk = chan->sk;
-
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
chan->psm, chan->dcid);
@@ -241,7 +318,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->conn = conn;
- if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) {
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
if (conn->hcon->type == LE_LINK) {
/* LE connection */
chan->omtu = L2CAP_LE_DEFAULT_MTU;
@@ -252,7 +329,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->scid = l2cap_alloc_cid(conn);
chan->omtu = L2CAP_DEFAULT_MTU;
}
- } else if (sk->sk_type == SOCK_DGRAM) {
+ } else if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
/* Connectionless socket */
chan->scid = L2CAP_CID_CONN_LESS;
chan->dcid = L2CAP_CID_CONN_LESS;
@@ -264,20 +341,20 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->omtu = L2CAP_DEFAULT_MTU;
}
- sock_hold(sk);
+ chan_hold(chan);
list_add(&chan->list, &conn->chan_l);
}
/* Delete channel.
* Must be called on the locked socket. */
-void l2cap_chan_del(struct l2cap_chan *chan, int err)
+static void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sock *parent = bt_sk(sk)->parent;
- l2cap_sock_clear_timer(sk);
+ __clear_chan_timer(chan);
BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
@@ -286,13 +363,13 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
write_lock_bh(&conn->chan_lock);
list_del(&chan->list);
write_unlock_bh(&conn->chan_lock);
- __sock_put(sk);
+ chan_put(chan);
chan->conn = NULL;
hci_conn_put(conn->hcon);
}
- sk->sk_state = BT_CLOSED;
+ l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);
if (err)
@@ -304,8 +381,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
} else
sk->sk_state_change(sk);
- if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE &&
- chan->conf_state & L2CAP_CONF_INPUT_DONE))
+ if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) &&
+ test_bit(CONF_INPUT_DONE, &chan->conf_state)))
return;
skb_queue_purge(&chan->tx_q);
@@ -313,9 +390,9 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
if (chan->mode == L2CAP_MODE_ERTM) {
struct srej_list *l, *tmp;
- del_timer(&chan->retrans_timer);
- del_timer(&chan->monitor_timer);
- del_timer(&chan->ack_timer);
+ __clear_retrans_timer(chan);
+ __clear_monitor_timer(chan);
+ __clear_ack_timer(chan);
skb_queue_purge(&chan->srej_q);
skb_queue_purge(&chan->busy_q);
@@ -327,11 +404,86 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
}
}
-static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
+static void l2cap_chan_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))) {
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ __clear_chan_timer(chan);
+ lock_sock(sk);
+ l2cap_chan_close(chan, ECONNRESET);
+ release_sock(sk);
+ chan->ops->close(chan->data);
+ }
+}
+
+void l2cap_chan_close(struct l2cap_chan *chan, int reason)
+{
+ struct l2cap_conn *conn = chan->conn;
struct sock *sk = chan->sk;
- if (sk->sk_type == SOCK_RAW) {
+ BT_DBG("chan %p state %d socket %p", chan, chan->state, sk->sk_socket);
+
+ switch (chan->state) {
+ case BT_LISTEN:
+ l2cap_chan_cleanup_listen(sk);
+
+ l2cap_state_change(chan, BT_CLOSED);
+ sock_set_flag(sk, SOCK_ZAPPED);
+ break;
+
+ case BT_CONNECTED:
+ case BT_CONFIG:
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
+ conn->hcon->type == ACL_LINK) {
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, sk->sk_sndtimeo);
+ l2cap_send_disconn_req(conn, chan, reason);
+ } else
+ l2cap_chan_del(chan, reason);
+ break;
+
+ case BT_CONNECT2:
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
+ 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;
+ l2cap_state_change(chan, BT_DISCONN);
+
+ 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 inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
+{
+ if (chan->chan_type == L2CAP_CHAN_RAW) {
switch (chan->sec_level) {
case BT_SECURITY_HIGH:
return HCI_AT_DEDICATED_BONDING_MITM;
@@ -371,7 +523,7 @@ static inline int l2cap_check_security(struct l2cap_chan *chan)
return hci_conn_security(conn->hcon, chan->sec_level, auth_type);
}
-u8 l2cap_get_ident(struct l2cap_conn *conn)
+static u8 l2cap_get_ident(struct l2cap_conn *conn)
{
u8 id;
@@ -393,7 +545,7 @@ u8 l2cap_get_ident(struct l2cap_conn *conn)
return id;
}
-void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data)
+static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data)
{
struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
u8 flags;
@@ -408,6 +560,8 @@ void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *d
else
flags = ACL_START;
+ bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON;
+
hci_send_acl(conn->hcon, skb, flags);
}
@@ -415,13 +569,11 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
{
struct sk_buff *skb;
struct l2cap_hdr *lh;
- struct l2cap_pinfo *pi = l2cap_pi(chan->sk);
struct l2cap_conn *conn = chan->conn;
- struct sock *sk = (struct sock *)pi;
int count, hlen = L2CAP_HDR_SIZE + 2;
u8 flags;
- if (sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_CONNECTED)
return;
if (chan->fcs == L2CAP_FCS_CRC16)
@@ -432,15 +584,11 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
count = min_t(unsigned int, conn->mtu, hlen);
control |= L2CAP_CTRL_FRAME_TYPE;
- if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control |= L2CAP_CTRL_FINAL;
- chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
- }
- if (chan->conn_state & L2CAP_CONN_SEND_PBIT) {
+ if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
control |= L2CAP_CTRL_POLL;
- chan->conn_state &= ~L2CAP_CONN_SEND_PBIT;
- }
skb = bt_skb_alloc(count, GFP_ATOMIC);
if (!skb)
@@ -461,14 +609,16 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
else
flags = ACL_START;
+ bt_cb(skb)->force_active = chan->force_active;
+
hci_send_acl(chan->conn->hcon, skb, flags);
}
static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
{
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
control |= L2CAP_SUPER_RCV_NOT_READY;
- chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ set_bit(CONN_RNR_SENT, &chan->conn_state);
} else
control |= L2CAP_SUPER_RCV_READY;
@@ -479,7 +629,7 @@ static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
{
- return !(chan->conf_state & L2CAP_CONF_CONNECT_PEND);
+ return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}
static void l2cap_do_start(struct l2cap_chan *chan)
@@ -497,7 +647,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
req.psm = chan->psm;
chan->ident = l2cap_get_ident(conn);
- chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
sizeof(req), &req);
@@ -533,7 +683,7 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
}
}
-void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err)
+static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err)
{
struct sock *sk;
struct l2cap_disconn_req req;
@@ -544,9 +694,9 @@ void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, in
sk = chan->sk;
if (chan->mode == L2CAP_MODE_ERTM) {
- del_timer(&chan->retrans_timer);
- del_timer(&chan->monitor_timer);
- del_timer(&chan->ack_timer);
+ __clear_retrans_timer(chan);
+ __clear_monitor_timer(chan);
+ __clear_ack_timer(chan);
}
req.dcid = cpu_to_le16(chan->dcid);
@@ -554,7 +704,7 @@ void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, in
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);
- sk->sk_state = BT_DISCONN;
+ l2cap_state_change(chan, BT_DISCONN);
sk->sk_err = err;
}
@@ -572,13 +722,12 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
bh_lock_sock(sk);
- if (sk->sk_type != SOCK_SEQPACKET &&
- sk->sk_type != SOCK_STREAM) {
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
bh_unlock_sock(sk);
continue;
}
- if (sk->sk_state == BT_CONNECT) {
+ if (chan->state == BT_CONNECT) {
struct l2cap_conn_req req;
if (!l2cap_check_security(chan) ||
@@ -587,14 +736,13 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
continue;
}
- if (!l2cap_mode_supported(chan->mode,
- conn->feat_mask)
- && chan->conf_state &
- L2CAP_CONF_STATE2_DEVICE) {
- /* __l2cap_sock_close() calls list_del(chan)
+ if (!l2cap_mode_supported(chan->mode, conn->feat_mask)
+ && test_bit(CONF_STATE2_DEVICE,
+ &chan->conf_state)) {
+ /* l2cap_chan_close() calls list_del(chan)
* so release the lock */
read_unlock_bh(&conn->chan_lock);
- __l2cap_sock_close(sk, ECONNRESET);
+ l2cap_chan_close(chan, ECONNRESET);
read_lock_bh(&conn->chan_lock);
bh_unlock_sock(sk);
continue;
@@ -604,12 +752,12 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
req.psm = chan->psm;
chan->ident = l2cap_get_ident(conn);
- chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
sizeof(req), &req);
- } else if (sk->sk_state == BT_CONNECT2) {
+ } else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
char buf[128];
rsp.scid = cpu_to_le16(chan->dcid);
@@ -623,7 +771,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
parent->sk_data_ready(parent, 0);
} else {
- sk->sk_state = BT_CONFIG;
+ l2cap_state_change(chan, BT_CONFIG);
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
@@ -635,13 +783,13 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
sizeof(rsp), &rsp);
- if (chan->conf_state & L2CAP_CONF_REQ_SENT ||
+ if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
rsp.result != L2CAP_CR_SUCCESS) {
bh_unlock_sock(sk);
continue;
}
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
@@ -665,7 +813,7 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdadd
list_for_each_entry(c, &chan_list, global_l) {
struct sock *sk = c->sk;
- if (state && sk->sk_state != state)
+ if (state && c->state != state)
continue;
if (c->scid == cid) {
@@ -709,24 +857,16 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
goto clean;
}
- sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
- if (!sk)
- goto clean;
-
- chan = l2cap_chan_create(sk);
- if (!chan) {
- l2cap_sock_kill(sk);
+ chan = pchan->ops->new_connection(pchan->data);
+ if (!chan)
goto clean;
- }
- l2cap_pi(sk)->chan = chan;
+ sk = chan->sk;
write_lock_bh(&conn->chan_lock);
hci_conn_hold(conn->hcon);
- l2cap_sock_init(sk, parent);
-
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
@@ -734,9 +874,9 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
__l2cap_chan_add(conn, chan);
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
+ __set_chan_timer(chan, sk->sk_sndtimeo);
- sk->sk_state = BT_CONNECTED;
+ l2cap_state_change(chan, BT_CONNECTED);
parent->sk_data_ready(parent, 0);
write_unlock_bh(&conn->chan_lock);
@@ -745,6 +885,23 @@ clean:
bh_unlock_sock(parent);
}
+static void l2cap_chan_ready(struct sock *sk)
+{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct sock *parent = bt_sk(sk)->parent;
+
+ BT_DBG("sk %p, parent %p", sk, parent);
+
+ chan->conf_state = 0;
+ __clear_chan_timer(chan);
+
+ l2cap_state_change(chan, BT_CONNECTED);
+ sk->sk_state_change(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+}
+
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan *chan;
@@ -762,17 +919,15 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
bh_lock_sock(sk);
if (conn->hcon->type == LE_LINK) {
- l2cap_sock_clear_timer(sk);
- sk->sk_state = BT_CONNECTED;
- sk->sk_state_change(sk);
- }
+ if (smp_conn_security(conn, chan->sec_level))
+ l2cap_chan_ready(sk);
- if (sk->sk_type != SOCK_SEQPACKET &&
- sk->sk_type != SOCK_STREAM) {
- l2cap_sock_clear_timer(sk);
- sk->sk_state = BT_CONNECTED;
+ } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ __clear_chan_timer(chan);
+ l2cap_state_change(chan, BT_CONNECTED);
sk->sk_state_change(sk);
- } else if (sk->sk_state == BT_CONNECT)
+
+ } else if (chan->state == BT_CONNECT)
l2cap_do_start(chan);
bh_unlock_sock(sk);
@@ -810,6 +965,45 @@ static void l2cap_info_timeout(unsigned long arg)
l2cap_conn_start(conn);
}
+static void l2cap_conn_del(struct hci_conn *hcon, int err)
+{
+ struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_chan *chan, *l;
+ struct sock *sk;
+
+ if (!conn)
+ return;
+
+ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
+ kfree_skb(conn->rx_skb);
+
+ /* Kill channels */
+ list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
+ sk = chan->sk;
+ bh_lock_sock(sk);
+ l2cap_chan_del(chan, err);
+ bh_unlock_sock(sk);
+ chan->ops->close(chan->data);
+ }
+
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
+ del_timer_sync(&conn->info_timer);
+
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ del_timer(&conn->security_timer);
+
+ hcon->l2cap_data = NULL;
+ kfree(conn);
+}
+
+static void security_timeout(unsigned long arg)
+{
+ struct l2cap_conn *conn = (void *) arg;
+
+ l2cap_conn_del(conn->hcon, ETIMEDOUT);
+}
+
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
{
struct l2cap_conn *conn = hcon->l2cap_data;
@@ -841,7 +1035,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
INIT_LIST_HEAD(&conn->chan_l);
- if (hcon->type != LE_LINK)
+ if (hcon->type == LE_LINK)
+ setup_timer(&conn->security_timer, security_timeout,
+ (unsigned long) conn);
+ else
setup_timer(&conn->info_timer, l2cap_info_timeout,
(unsigned long) conn);
@@ -850,35 +1047,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
return conn;
}
-static void l2cap_conn_del(struct hci_conn *hcon, int err)
-{
- struct l2cap_conn *conn = hcon->l2cap_data;
- struct l2cap_chan *chan, *l;
- struct sock *sk;
-
- if (!conn)
- return;
-
- BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
-
- kfree_skb(conn->rx_skb);
-
- /* Kill channels */
- list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
- sk = chan->sk;
- bh_lock_sock(sk);
- l2cap_chan_del(chan, err);
- bh_unlock_sock(sk);
- l2cap_sock_kill(sk);
- }
-
- if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
- del_timer_sync(&conn->info_timer);
-
- hcon->l2cap_data = NULL;
- kfree(conn);
-}
-
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
write_lock_bh(&conn->chan_lock);
@@ -900,7 +1068,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
list_for_each_entry(c, &chan_list, global_l) {
struct sock *sk = c->sk;
- if (state && sk->sk_state != state)
+ if (state && c->state != state)
continue;
if (c->psm == psm) {
@@ -967,15 +1135,14 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
l2cap_chan_add(conn, chan);
- sk->sk_state = BT_CONNECT;
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
+ l2cap_state_change(chan, BT_CONNECT);
+ __set_chan_timer(chan, sk->sk_sndtimeo);
if (hcon->state == BT_CONNECTED) {
- if (sk->sk_type != SOCK_SEQPACKET &&
- sk->sk_type != SOCK_STREAM) {
- l2cap_sock_clear_timer(sk);
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ __clear_chan_timer(chan);
if (l2cap_check_security(chan))
- sk->sk_state = BT_CONNECTED;
+ l2cap_state_change(chan, BT_CONNECTED);
} else
l2cap_do_start(chan);
}
@@ -1035,7 +1202,7 @@ static void l2cap_monitor_timeout(unsigned long arg)
}
chan->retry_count++;
- __mod_monitor_timer();
+ __set_monitor_timer(chan);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
bh_unlock_sock(sk);
@@ -1050,9 +1217,9 @@ static void l2cap_retrans_timeout(unsigned long arg)
bh_lock_sock(sk);
chan->retry_count = 1;
- __mod_monitor_timer();
+ __set_monitor_timer(chan);
- chan->conn_state |= L2CAP_CONN_WAIT_F;
+ set_bit(CONN_WAIT_F, &chan->conn_state);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
bh_unlock_sock(sk);
@@ -1074,7 +1241,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
}
if (!chan->unacked_frames)
- del_timer(&chan->retrans_timer);
+ __clear_retrans_timer(chan);
}
void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1089,6 +1256,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
else
flags = ACL_START;
+ bt_cb(skb)->force_active = chan->force_active;
hci_send_acl(hcon, skb, flags);
}
@@ -1142,10 +1310,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
control &= L2CAP_CTRL_SAR;
- if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control |= L2CAP_CTRL_FINAL;
- chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
- }
control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
| (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
@@ -1163,11 +1329,10 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
int l2cap_ertm_send(struct l2cap_chan *chan)
{
struct sk_buff *skb, *tx_skb;
- struct sock *sk = chan->sk;
u16 control, fcs;
int nsent = 0;
- if (sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_CONNECTED)
return -ENOTCONN;
while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) {
@@ -1185,10 +1350,9 @@ int l2cap_ertm_send(struct l2cap_chan *chan)
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
control &= L2CAP_CTRL_SAR;
- if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control |= L2CAP_CTRL_FINAL;
- chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
- }
+
control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
| (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
@@ -1201,7 +1365,7 @@ int l2cap_ertm_send(struct l2cap_chan *chan)
l2cap_do_send(chan, tx_skb);
- __mod_retrans_timer();
+ __set_retrans_timer(chan);
bt_cb(skb)->tx_seq = chan->next_tx_seq;
chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
@@ -1240,9 +1404,9 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
control |= L2CAP_SUPER_RCV_NOT_READY;
- chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ set_bit(CONN_RNR_SENT, &chan->conn_state);
l2cap_send_sframe(chan, control);
return;
}
@@ -1450,28 +1614,83 @@ int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t le
return size;
}
-static void l2cap_chan_ready(struct sock *sk)
+int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
- struct sock *parent = bt_sk(sk)->parent;
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct sk_buff *skb;
+ u16 control;
+ int err;
- BT_DBG("sk %p, parent %p", sk, parent);
+ /* Connectionless channel */
+ if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
+ skb = l2cap_create_connless_pdu(chan, msg, len);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
- chan->conf_state = 0;
- l2cap_sock_clear_timer(sk);
+ l2cap_do_send(chan, skb);
+ return len;
+ }
- if (!parent) {
- /* Outgoing channel.
- * Wake up socket sleeping on connect.
- */
- sk->sk_state = BT_CONNECTED;
- sk->sk_state_change(sk);
- } else {
- /* Incoming channel.
- * Wake up socket sleeping on accept.
- */
- parent->sk_data_ready(parent, 0);
+ switch (chan->mode) {
+ case L2CAP_MODE_BASIC:
+ /* Check outgoing MTU */
+ if (len > chan->omtu)
+ return -EMSGSIZE;
+
+ /* Create a basic PDU */
+ skb = l2cap_create_basic_pdu(chan, msg, len);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ 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))
+ return PTR_ERR(skb);
+
+ __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)
+ return err;
+ }
+
+ if (chan->mode == L2CAP_MODE_STREAMING) {
+ l2cap_streaming_send(chan);
+ err = len;
+ break;
+ }
+
+ if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
+ test_bit(CONN_WAIT_F, &chan->conn_state)) {
+ 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;
}
+
+ return err;
}
/* Copy frame to all raw sockets on that connection */
@@ -1485,7 +1704,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
read_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;
- if (sk->sk_type != SOCK_RAW)
+ if (chan->chan_type != L2CAP_CHAN_RAW)
continue;
/* Don't send frame to the socket it came from */
@@ -1495,7 +1714,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
if (!nskb)
continue;
- if (sock_queue_rcv_skb(sk, nskb))
+ if (chan->ops->recv(chan->data, nskb))
kfree_skb(nskb);
}
read_unlock(&conn->chan_lock);
@@ -1690,7 +1909,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
switch (chan->mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
- if (chan->conf_state & L2CAP_CONF_STATE2_DEVICE)
+ if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
break;
/* fall through */
@@ -1737,7 +1956,7 @@ done:
break;
if (chan->fcs == L2CAP_FCS_NONE ||
- chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
@@ -1760,7 +1979,7 @@ done:
break;
if (chan->fcs == L2CAP_FCS_NONE ||
- chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
@@ -1812,7 +2031,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
case L2CAP_CONF_FCS:
if (val == L2CAP_FCS_NONE)
- chan->conf_state |= L2CAP_CONF_NO_FCS_RECV;
+ set_bit(CONF_NO_FCS_RECV, &chan->conf_state);
break;
@@ -1832,7 +2051,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
switch (chan->mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
- if (!(chan->conf_state & L2CAP_CONF_STATE2_DEVICE)) {
+ if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) {
chan->mode = l2cap_select_mode(rfc.mode,
chan->conn->feat_mask);
break;
@@ -1865,14 +2084,14 @@ done:
result = L2CAP_CONF_UNACCEPT;
else {
chan->omtu = mtu;
- chan->conf_state |= L2CAP_CONF_MTU_DONE;
+ set_bit(CONF_MTU_DONE, &chan->conf_state);
}
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
switch (rfc.mode) {
case L2CAP_MODE_BASIC:
chan->fcs = L2CAP_FCS_NONE;
- chan->conf_state |= L2CAP_CONF_MODE_DONE;
+ set_bit(CONF_MODE_DONE, &chan->conf_state);
break;
case L2CAP_MODE_ERTM:
@@ -1889,7 +2108,7 @@ done:
rfc.monitor_timeout =
le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO);
- chan->conf_state |= L2CAP_CONF_MODE_DONE;
+ set_bit(CONF_MODE_DONE, &chan->conf_state);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
@@ -1902,7 +2121,7 @@ done:
chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
- chan->conf_state |= L2CAP_CONF_MODE_DONE;
+ set_bit(CONF_MODE_DONE, &chan->conf_state);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
@@ -1917,7 +2136,7 @@ done:
}
if (result == L2CAP_CONF_SUCCESS)
- chan->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+ set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
}
rsp->scid = cpu_to_le16(chan->dcid);
rsp->result = cpu_to_le16(result);
@@ -1959,7 +2178,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
if (olen == sizeof(rfc))
memcpy(&rfc, (void *)val, olen);
- if ((chan->conf_state & L2CAP_CONF_STATE2_DEVICE) &&
+ if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) &&
rfc.mode != chan->mode)
return -ECONNREFUSED;
@@ -2021,10 +2240,9 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
l2cap_send_cmd(conn, chan->ident,
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
- if (chan->conf_state & L2CAP_CONF_REQ_SENT)
+ if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
return;
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
@@ -2124,17 +2342,11 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
goto response;
}
- sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
- if (!sk)
- goto response;
-
- chan = l2cap_chan_create(sk);
- if (!chan) {
- l2cap_sock_kill(sk);
+ chan = pchan->ops->new_connection(pchan->data);
+ if (!chan)
goto response;
- }
- l2cap_pi(sk)->chan = chan;
+ sk = chan->sk;
write_lock_bh(&conn->chan_lock);
@@ -2142,13 +2354,12 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
if (__l2cap_get_chan_by_dcid(conn, scid)) {
write_unlock_bh(&conn->chan_lock);
sock_set_flag(sk, SOCK_ZAPPED);
- l2cap_sock_kill(sk);
+ chan->ops->close(chan->data);
goto response;
}
hci_conn_hold(conn->hcon);
- l2cap_sock_init(sk, parent);
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
chan->psm = psm;
@@ -2160,29 +2371,29 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
dcid = chan->scid;
- l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
+ __set_chan_timer(chan, sk->sk_sndtimeo);
chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
- sk->sk_state = BT_CONNECT2;
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
parent->sk_data_ready(parent, 0);
} else {
- sk->sk_state = BT_CONFIG;
+ l2cap_state_change(chan, BT_CONFIG);
result = L2CAP_CR_SUCCESS;
status = L2CAP_CS_NO_INFO;
}
} else {
- sk->sk_state = BT_CONNECT2;
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHEN_PEND;
}
} else {
- sk->sk_state = BT_CONNECT2;
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_NO_INFO;
}
@@ -2213,10 +2424,10 @@ sendresp:
L2CAP_INFO_REQ, sizeof(info), &info);
}
- if (chan && !(chan->conf_state & L2CAP_CONF_REQ_SENT) &&
+ if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) &&
result == L2CAP_CR_SUCCESS) {
u8 buf[128];
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
@@ -2254,31 +2465,29 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
switch (result) {
case L2CAP_CR_SUCCESS:
- sk->sk_state = BT_CONFIG;
+ l2cap_state_change(chan, BT_CONFIG);
chan->ident = 0;
chan->dcid = dcid;
- chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
+ clear_bit(CONF_CONNECT_PEND, &chan->conf_state);
- if (chan->conf_state & L2CAP_CONF_REQ_SENT)
+ if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
break;
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
-
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, req), req);
chan->num_conf_req++;
break;
case L2CAP_CR_PEND:
- chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
break;
default:
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
- sk->sk_state = BT_DISCONN;
- l2cap_sock_clear_timer(sk);
- l2cap_sock_set_timer(sk, HZ / 5);
+ l2cap_state_change(chan, BT_DISCONN);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ / 5);
break;
}
@@ -2292,14 +2501,12 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
static inline void set_default_fcs(struct l2cap_chan *chan)
{
- struct l2cap_pinfo *pi = l2cap_pi(chan->sk);
-
/* FCS is enabled only in ERTM or streaming mode, if one or both
* sides request it.
*/
if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING)
chan->fcs = L2CAP_FCS_NONE;
- else if (!(pi->chan->conf_state & L2CAP_CONF_NO_FCS_RECV))
+ else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state))
chan->fcs = L2CAP_FCS_CRC16;
}
@@ -2323,8 +2530,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
sk = chan->sk;
- if ((bt_sk(sk)->defer_setup && sk->sk_state != BT_CONNECT2) ||
- (!bt_sk(sk)->defer_setup && sk->sk_state != BT_CONFIG)) {
+ if ((bt_sk(sk)->defer_setup && chan->state != BT_CONNECT2) ||
+ (!bt_sk(sk)->defer_setup && chan->state != BT_CONFIG)) {
struct l2cap_cmd_rej rej;
rej.reason = cpu_to_le16(0x0002);
@@ -2367,13 +2574,13 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
/* Reset config buffer. */
chan->conf_len = 0;
- if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE))
+ if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state))
goto unlock;
- if (chan->conf_state & L2CAP_CONF_INPUT_DONE) {
+ if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);
- sk->sk_state = BT_CONNECTED;
+ l2cap_state_change(chan, BT_CONNECTED);
chan->next_tx_seq = 0;
chan->expected_tx_seq = 0;
@@ -2385,9 +2592,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
goto unlock;
}
- if (!(chan->conf_state & L2CAP_CONF_REQ_SENT)) {
+ if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) {
u8 buf[64];
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
@@ -2452,7 +2658,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
default:
sk->sk_err = ECONNRESET;
- l2cap_sock_set_timer(sk, HZ * 5);
+ __set_chan_timer(chan, HZ * 5);
l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
}
@@ -2460,12 +2666,12 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (flags & 0x01)
goto done;
- chan->conf_state |= L2CAP_CONF_INPUT_DONE;
+ set_bit(CONF_INPUT_DONE, &chan->conf_state);
- if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+ if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);
- sk->sk_state = BT_CONNECTED;
+ l2cap_state_change(chan, BT_CONNECTED);
chan->next_tx_seq = 0;
chan->expected_tx_seq = 0;
skb_queue_head_init(&chan->tx_q);
@@ -2507,9 +2713,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
- sk->sk_state = BT_DISCONN;
- l2cap_sock_clear_timer(sk);
- l2cap_sock_set_timer(sk, HZ / 5);
+ l2cap_state_change(chan, BT_DISCONN);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ / 5);
bh_unlock_sock(sk);
return 0;
}
@@ -2517,7 +2723,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
l2cap_chan_del(chan, ECONNRESET);
bh_unlock_sock(sk);
- l2cap_sock_kill(sk);
+ chan->ops->close(chan->data);
return 0;
}
@@ -2541,9 +2747,9 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
- sk->sk_state = BT_DISCONN;
- l2cap_sock_clear_timer(sk);
- l2cap_sock_set_timer(sk, HZ / 5);
+ l2cap_state_change(chan,BT_DISCONN);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ / 5);
bh_unlock_sock(sk);
return 0;
}
@@ -2551,7 +2757,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
l2cap_chan_del(chan, 0);
bh_unlock_sock(sk);
- l2cap_sock_kill(sk);
+ chan->ops->close(chan->data);
return 0;
}
@@ -2859,18 +3065,18 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
control |= L2CAP_SUPER_RCV_NOT_READY;
l2cap_send_sframe(chan, control);
- chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ set_bit(CONN_RNR_SENT, &chan->conn_state);
}
- if (chan->conn_state & L2CAP_CONN_REMOTE_BUSY)
+ if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
l2cap_retransmit_frames(chan);
l2cap_ertm_send(chan);
- if (!(chan->conn_state & L2CAP_CONN_LOCAL_BUSY) &&
+ if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
chan->frames_sent == 0) {
control |= L2CAP_SUPER_RCV_READY;
l2cap_send_sframe(chan, control);
@@ -2926,17 +3132,13 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
switch (control & L2CAP_CTRL_SAR) {
case L2CAP_SDU_UNSEGMENTED:
- if (chan->conn_state & L2CAP_CONN_SAR_SDU)
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state))
goto drop;
- err = sock_queue_rcv_skb(chan->sk, skb);
- if (!err)
- return err;
-
- break;
+ return chan->ops->recv(chan->data, skb);
case L2CAP_SDU_START:
- if (chan->conn_state & L2CAP_CONN_SAR_SDU)
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state))
goto drop;
chan->sdu_len = get_unaligned_le16(skb->data);
@@ -2955,12 +3157,12 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- chan->conn_state |= L2CAP_CONN_SAR_SDU;
+ set_bit(CONN_SAR_SDU, &chan->conn_state);
chan->partial_sdu_len = skb->len;
break;
case L2CAP_SDU_CONTINUE:
- if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
goto disconnect;
if (!chan->sdu)
@@ -2975,13 +3177,13 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
break;
case L2CAP_SDU_END:
- if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
goto disconnect;
if (!chan->sdu)
goto disconnect;
- if (!(chan->conn_state & L2CAP_CONN_SAR_RETRY)) {
+ if (!test_bit(CONN_SAR_RETRY, &chan->conn_state)) {
chan->partial_sdu_len += skb->len;
if (chan->partial_sdu_len > chan->imtu)
@@ -2995,19 +3197,19 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
_skb = skb_clone(chan->sdu, GFP_ATOMIC);
if (!_skb) {
- chan->conn_state |= L2CAP_CONN_SAR_RETRY;
+ set_bit(CONN_SAR_RETRY, &chan->conn_state);
return -ENOMEM;
}
- err = sock_queue_rcv_skb(chan->sk, _skb);
+ err = chan->ops->recv(chan->data, _skb);
if (err < 0) {
kfree_skb(_skb);
- chan->conn_state |= L2CAP_CONN_SAR_RETRY;
+ set_bit(CONN_SAR_RETRY, &chan->conn_state);
return err;
}
- chan->conn_state &= ~L2CAP_CONN_SAR_RETRY;
- chan->conn_state &= ~L2CAP_CONN_SAR_SDU;
+ clear_bit(CONN_SAR_RETRY, &chan->conn_state);
+ clear_bit(CONN_SAR_SDU, &chan->conn_state);
kfree_skb(chan->sdu);
break;
@@ -3043,7 +3245,7 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan)
chan->buffer_seq = (chan->buffer_seq + 1) % 64;
}
- if (!(chan->conn_state & L2CAP_CONN_RNR_SENT))
+ if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
goto done;
control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
@@ -3051,14 +3253,14 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan)
l2cap_send_sframe(chan, control);
chan->retry_count = 1;
- del_timer(&chan->retrans_timer);
- __mod_monitor_timer();
+ __clear_retrans_timer(chan);
+ __set_monitor_timer(chan);
- chan->conn_state |= L2CAP_CONN_WAIT_F;
+ set_bit(CONN_WAIT_F, &chan->conn_state);
done:
- chan->conn_state &= ~L2CAP_CONN_LOCAL_BUSY;
- chan->conn_state &= ~L2CAP_CONN_RNR_SENT;
+ clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+ clear_bit(CONN_RNR_SENT, &chan->conn_state);
BT_DBG("chan %p, Exit local busy", chan);
@@ -3116,7 +3318,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c
{
int sctrl, err;
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
__skb_queue_tail(&chan->busy_q, skb);
return l2cap_try_push_rx_skb(chan);
@@ -3133,7 +3335,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c
/* Busy Condition */
BT_DBG("chan %p, Enter local busy", chan);
- chan->conn_state |= L2CAP_CONN_LOCAL_BUSY;
+ set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
__skb_queue_tail(&chan->busy_q, skb);
@@ -3141,9 +3343,9 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c
sctrl |= L2CAP_SUPER_RCV_NOT_READY;
l2cap_send_sframe(chan, sctrl);
- chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ set_bit(CONN_RNR_SENT, &chan->conn_state);
- del_timer(&chan->ack_timer);
+ __clear_ack_timer(chan);
queue_work(_busy_wq, &chan->busy_work);
@@ -3162,19 +3364,19 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf
switch (control & L2CAP_CTRL_SAR) {
case L2CAP_SDU_UNSEGMENTED:
- if (chan->conn_state & L2CAP_CONN_SAR_SDU) {
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
kfree_skb(chan->sdu);
break;
}
- err = sock_queue_rcv_skb(chan->sk, skb);
+ err = chan->ops->recv(chan->data, skb);
if (!err)
return 0;
break;
case L2CAP_SDU_START:
- if (chan->conn_state & L2CAP_CONN_SAR_SDU) {
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
kfree_skb(chan->sdu);
break;
}
@@ -3195,13 +3397,13 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- chan->conn_state |= L2CAP_CONN_SAR_SDU;
+ set_bit(CONN_SAR_SDU, &chan->conn_state);
chan->partial_sdu_len = skb->len;
err = 0;
break;
case L2CAP_SDU_CONTINUE:
- if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
break;
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
@@ -3215,12 +3417,12 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf
break;
case L2CAP_SDU_END:
- if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
break;
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- chan->conn_state &= ~L2CAP_CONN_SAR_SDU;
+ clear_bit(CONN_SAR_SDU, &chan->conn_state);
chan->partial_sdu_len += skb->len;
if (chan->partial_sdu_len > chan->imtu)
@@ -3228,7 +3430,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf
if (chan->partial_sdu_len == chan->sdu_len) {
_skb = skb_clone(chan->sdu, GFP_ATOMIC);
- err = sock_queue_rcv_skb(chan->sk, _skb);
+ err = chan->ops->recv(chan->data, _skb);
if (err < 0)
kfree_skb(_skb);
}
@@ -3311,11 +3513,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
tx_seq, rx_control);
if (L2CAP_CTRL_FINAL & rx_control &&
- chan->conn_state & L2CAP_CONN_WAIT_F) {
- del_timer(&chan->monitor_timer);
+ test_bit(CONN_WAIT_F, &chan->conn_state)) {
+ __clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
- __mod_retrans_timer();
- chan->conn_state &= ~L2CAP_CONN_WAIT_F;
+ __set_retrans_timer(chan);
+ clear_bit(CONN_WAIT_F, &chan->conn_state);
}
chan->expected_ack_seq = req_seq;
@@ -3334,10 +3536,10 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
goto drop;
}
- if (chan->conn_state == L2CAP_CONN_LOCAL_BUSY)
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
goto drop;
- if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
+ if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
struct srej_list *first;
first = list_first_entry(&chan->srej_l,
@@ -3351,7 +3553,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
if (list_empty(&chan->srej_l)) {
chan->buffer_seq = chan->buffer_seq_srej;
- chan->conn_state &= ~L2CAP_CONN_SREJ_SENT;
+ clear_bit(CONN_SREJ_SENT, &chan->conn_state);
l2cap_send_ack(chan);
BT_DBG("chan %p, Exit SREJ_SENT", chan);
}
@@ -3380,7 +3582,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
if (tx_seq_offset < expected_tx_seq_offset)
goto drop;
- chan->conn_state |= L2CAP_CONN_SREJ_SENT;
+ set_bit(CONN_SREJ_SENT, &chan->conn_state);
BT_DBG("chan %p, Enter SREJ", chan);
@@ -3391,18 +3593,18 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
__skb_queue_head_init(&chan->busy_q);
l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
- chan->conn_state |= L2CAP_CONN_SEND_PBIT;
+ set_bit(CONN_SEND_PBIT, &chan->conn_state);
l2cap_send_srejframe(chan, tx_seq);
- del_timer(&chan->ack_timer);
+ __clear_ack_timer(chan);
}
return 0;
expected:
chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
- if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
+ if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
bt_cb(skb)->tx_seq = tx_seq;
bt_cb(skb)->sar = sar;
__skb_queue_tail(&chan->srej_q, skb);
@@ -3414,13 +3616,11 @@ expected:
return 0;
if (rx_control & L2CAP_CTRL_FINAL) {
- if (chan->conn_state & L2CAP_CONN_REJ_ACT)
- chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
- else
+ if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
}
- __mod_ack_timer();
+ __set_ack_timer(chan);
chan->num_acked = (chan->num_acked + 1) % num_to_ack;
if (chan->num_acked == num_to_ack - 1)
@@ -3442,33 +3642,31 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co
l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_POLL) {
- chan->conn_state |= L2CAP_CONN_SEND_FBIT;
- if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
- if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
+ if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
+ if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
(chan->unacked_frames > 0))
- __mod_retrans_timer();
+ __set_retrans_timer(chan);
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
l2cap_send_srejtail(chan);
} else {
l2cap_send_i_or_rr_or_rnr(chan);
}
} else if (rx_control & L2CAP_CTRL_FINAL) {
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
- if (chan->conn_state & L2CAP_CONN_REJ_ACT)
- chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
- else
+ if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
} else {
- if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+ if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
(chan->unacked_frames > 0))
- __mod_retrans_timer();
+ __set_retrans_timer(chan);
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
- if (chan->conn_state & L2CAP_CONN_SREJ_SENT)
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+ if (test_bit(CONN_SREJ_SENT, &chan->conn_state))
l2cap_send_ack(chan);
else
l2cap_ertm_send(chan);
@@ -3481,21 +3679,19 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c
BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_FINAL) {
- if (chan->conn_state & L2CAP_CONN_REJ_ACT)
- chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
- else
+ if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
} else {
l2cap_retransmit_frames(chan);
- if (chan->conn_state & L2CAP_CONN_WAIT_F)
- chan->conn_state |= L2CAP_CONN_REJ_ACT;
+ if (test_bit(CONN_WAIT_F, &chan->conn_state))
+ set_bit(CONN_REJ_ACT, &chan->conn_state);
}
}
static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control)
@@ -3504,32 +3700,32 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_
BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
if (rx_control & L2CAP_CTRL_POLL) {
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
- chan->conn_state |= L2CAP_CONN_SEND_FBIT;
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
l2cap_retransmit_one_frame(chan, tx_seq);
l2cap_ertm_send(chan);
- if (chan->conn_state & L2CAP_CONN_WAIT_F) {
+ if (test_bit(CONN_WAIT_F, &chan->conn_state)) {
chan->srej_save_reqseq = tx_seq;
- chan->conn_state |= L2CAP_CONN_SREJ_ACT;
+ set_bit(CONN_SREJ_ACT, &chan->conn_state);
}
} else if (rx_control & L2CAP_CTRL_FINAL) {
- if ((chan->conn_state & L2CAP_CONN_SREJ_ACT) &&
+ if (test_bit(CONN_SREJ_ACT, &chan->conn_state) &&
chan->srej_save_reqseq == tx_seq)
- chan->conn_state &= ~L2CAP_CONN_SREJ_ACT;
+ clear_bit(CONN_SREJ_ACT, &chan->conn_state);
else
l2cap_retransmit_one_frame(chan, tx_seq);
} else {
l2cap_retransmit_one_frame(chan, tx_seq);
- if (chan->conn_state & L2CAP_CONN_WAIT_F) {
+ if (test_bit(CONN_WAIT_F, &chan->conn_state)) {
chan->srej_save_reqseq = tx_seq;
- chan->conn_state |= L2CAP_CONN_SREJ_ACT;
+ set_bit(CONN_SREJ_ACT, &chan->conn_state);
}
}
}
@@ -3540,15 +3736,15 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c
BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- chan->conn_state |= L2CAP_CONN_REMOTE_BUSY;
+ set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_POLL)
- chan->conn_state |= L2CAP_CONN_SEND_FBIT;
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
- if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) {
- del_timer(&chan->retrans_timer);
+ if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
+ __clear_retrans_timer(chan);
if (rx_control & L2CAP_CTRL_POLL)
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL);
return;
@@ -3565,11 +3761,11 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont
BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len);
if (L2CAP_CTRL_FINAL & rx_control &&
- chan->conn_state & L2CAP_CONN_WAIT_F) {
- del_timer(&chan->monitor_timer);
+ test_bit(CONN_WAIT_F, &chan->conn_state)) {
+ __clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
- __mod_retrans_timer();
- chan->conn_state &= ~L2CAP_CONN_WAIT_F;
+ __set_retrans_timer(chan);
+ clear_bit(CONN_WAIT_F, &chan->conn_state);
}
switch (rx_control & L2CAP_CTRL_SUPERVISE) {
@@ -3668,7 +3864,6 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
{
struct l2cap_chan *chan;
struct sock *sk = NULL;
- struct l2cap_pinfo *pi;
u16 control;
u8 tx_seq;
int len;
@@ -3680,11 +3875,10 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
}
sk = chan->sk;
- pi = l2cap_pi(sk);
BT_DBG("chan %p, len %d", chan, skb->len);
- if (sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_CONNECTED)
goto drop;
switch (chan->mode) {
@@ -3697,7 +3891,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
if (chan->imtu < skb->len)
goto drop;
- if (!sock_queue_rcv_skb(sk, skb))
+ if (!chan->ops->recv(chan->data, skb))
goto done;
break;
@@ -3769,13 +3963,13 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
BT_DBG("sk %p, len %d", sk, skb->len);
- if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
- if (l2cap_pi(sk)->chan->imtu < skb->len)
+ if (chan->imtu < skb->len)
goto drop;
- if (!sock_queue_rcv_skb(sk, skb))
+ if (!chan->ops->recv(chan->data, skb))
goto done;
drop:
@@ -3802,13 +3996,13 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct
BT_DBG("sk %p, len %d", sk, skb->len);
- if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
- if (l2cap_pi(sk)->chan->imtu < skb->len)
+ if (chan->imtu < skb->len)
goto drop;
- if (!sock_queue_rcv_skb(sk, skb))
+ if (!chan->ops->recv(chan->data, skb))
goto done;
drop:
@@ -3853,6 +4047,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_att_channel(conn, cid, skb);
break;
+ case L2CAP_CID_SMP:
+ if (smp_sig_channel(conn, skb))
+ l2cap_conn_del(conn->hcon, EACCES);
+ break;
+
default:
l2cap_data_channel(conn, cid, skb);
break;
@@ -3876,7 +4075,7 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
list_for_each_entry(c, &chan_list, global_l) {
struct sock *sk = c->sk;
- if (sk->sk_state != BT_LISTEN)
+ if (c->state != BT_LISTEN)
continue;
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
@@ -3920,7 +4119,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon)
BT_DBG("hcon %p", hcon);
- if (hcon->type != ACL_LINK || !conn)
+ if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn)
return 0x13;
return conn->disc_reason;
@@ -3940,20 +4139,18 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
{
- struct sock *sk = chan->sk;
-
- if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM)
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED)
return;
if (encrypt == 0x00) {
if (chan->sec_level == BT_SECURITY_MEDIUM) {
- l2cap_sock_clear_timer(sk);
- l2cap_sock_set_timer(sk, HZ * 5);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ * 5);
} else if (chan->sec_level == BT_SECURITY_HIGH)
- __l2cap_sock_close(sk, ECONNREFUSED);
+ l2cap_chan_close(chan, ECONNREFUSED);
} else {
if (chan->sec_level == BT_SECURITY_MEDIUM)
- l2cap_sock_clear_timer(sk);
+ __clear_chan_timer(chan);
}
}
@@ -3974,34 +4171,47 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
bh_lock_sock(sk);
- if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) {
+ BT_DBG("chan->scid %d", chan->scid);
+
+ if (chan->scid == L2CAP_CID_LE_DATA) {
+ if (!status && encrypt) {
+ chan->sec_level = hcon->sec_level;
+ del_timer(&conn->security_timer);
+ l2cap_chan_ready(sk);
+ }
+
bh_unlock_sock(sk);
continue;
}
- if (!status && (sk->sk_state == BT_CONNECTED ||
- sk->sk_state == BT_CONFIG)) {
+ if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
+ bh_unlock_sock(sk);
+ continue;
+ }
+
+ if (!status && (chan->state == BT_CONNECTED ||
+ chan->state == BT_CONFIG)) {
l2cap_check_encryption(chan, encrypt);
bh_unlock_sock(sk);
continue;
}
- if (sk->sk_state == BT_CONNECT) {
+ if (chan->state == BT_CONNECT) {
if (!status) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(chan->scid);
req.psm = chan->psm;
chan->ident = l2cap_get_ident(conn);
- chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
l2cap_send_cmd(conn, chan->ident,
L2CAP_CONN_REQ, sizeof(req), &req);
} else {
- l2cap_sock_clear_timer(sk);
- l2cap_sock_set_timer(sk, HZ / 10);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ / 10);
}
- } else if (sk->sk_state == BT_CONNECT2) {
+ } else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
__u16 res, stat;
@@ -4012,13 +4222,13 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
stat = L2CAP_CS_AUTHOR_PEND;
parent->sk_data_ready(parent, 0);
} else {
- sk->sk_state = BT_CONFIG;
+ l2cap_state_change(chan, BT_CONFIG);
res = L2CAP_CR_SUCCESS;
stat = L2CAP_CS_NO_INFO;
}
} else {
- sk->sk_state = BT_DISCONN;
- l2cap_sock_set_timer(sk, HZ / 10);
+ l2cap_state_change(chan, BT_DISCONN);
+ __set_chan_timer(chan, HZ / 10);
res = L2CAP_CR_SEC_BLOCK;
stat = L2CAP_CS_NO_INFO;
}
@@ -4162,10 +4372,10 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
batostr(&bt_sk(sk)->src),
batostr(&bt_sk(sk)->dst),
- sk->sk_state, __le16_to_cpu(c->psm),
+ c->state, __le16_to_cpu(c->psm),
c->scid, c->dcid, c->imtu, c->omtu,
c->sec_level, c->mode);
- }
+}
read_unlock_bh(&chan_list_lock);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 8248303f44e8..39082d4e77ce 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -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;
- }
-
- /* 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;
+ release_sock(sk);
+ return -ENOTCONN;
}
- 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;
- }
+ err = l2cap_chan_send(chan, msg, len);
-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;
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index dae382ce7020..64c0418a6221 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -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);
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 1b10727ce523..8f01e6b11a70 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -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);
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
new file mode 100644
index 000000000000..a36f8707d964
--- /dev/null
+++ b/net/bluetooth/smp.c
@@ -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;
+}
diff --git a/net/core/sock.c b/net/core/sock.c
index 6e819780c232..84d6de809352 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -158,7 +158,7 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
"sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG" ,
- "sk_lock-AF_MAX"
+ "sk_lock-AF_NFC" , "sk_lock-AF_MAX"
};
static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
@@ -174,7 +174,7 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
"slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" ,
- "slock-AF_MAX"
+ "slock-AF_NFC" , "slock-AF_MAX"
};
static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" ,
@@ -190,7 +190,7 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
"clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG" ,
- "clock-AF_MAX"
+ "clock-AF_NFC" , "clock-AF_MAX"
};
/*
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index b9b595c08112..0785e95c9924 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -11,6 +11,7 @@
#include <linux/types.h>
#include <linux/crypto.h>
#include <linux/err.h>
+#include <crypto/aes.h>
#include <net/mac80211.h>
#include "key.h"
@@ -21,21 +22,21 @@ static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
int i;
u8 *b_0, *aad, *b, *s_0;
- b_0 = scratch + 3 * AES_BLOCK_LEN;
- aad = scratch + 4 * AES_BLOCK_LEN;
+ b_0 = scratch + 3 * AES_BLOCK_SIZE;
+ aad = scratch + 4 * AES_BLOCK_SIZE;
b = scratch;
- s_0 = scratch + AES_BLOCK_LEN;
+ s_0 = scratch + AES_BLOCK_SIZE;
crypto_cipher_encrypt_one(tfm, b, b_0);
/* Extra Authenticate-only data (always two AES blocks) */
- for (i = 0; i < AES_BLOCK_LEN; i++)
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
aad[i] ^= b[i];
crypto_cipher_encrypt_one(tfm, b, aad);
- aad += AES_BLOCK_LEN;
+ aad += AES_BLOCK_SIZE;
- for (i = 0; i < AES_BLOCK_LEN; i++)
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
aad[i] ^= b[i];
crypto_cipher_encrypt_one(tfm, a, aad);
@@ -57,12 +58,12 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
u8 *pos, *cpos, *b, *s_0, *e, *b_0;
b = scratch;
- s_0 = scratch + AES_BLOCK_LEN;
- e = scratch + 2 * AES_BLOCK_LEN;
- b_0 = scratch + 3 * AES_BLOCK_LEN;
+ s_0 = scratch + AES_BLOCK_SIZE;
+ e = scratch + 2 * AES_BLOCK_SIZE;
+ b_0 = scratch + 3 * AES_BLOCK_SIZE;
- num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
- last_len = data_len % AES_BLOCK_LEN;
+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
+ last_len = data_len % AES_BLOCK_SIZE;
aes_ccm_prepare(tfm, scratch, b);
/* Process payload blocks */
@@ -70,7 +71,7 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
cpos = cdata;
for (j = 1; j <= num_blocks; j++) {
int blen = (j == num_blocks && last_len) ?
- last_len : AES_BLOCK_LEN;
+ last_len : AES_BLOCK_SIZE;
/* Authentication followed by encryption */
for (i = 0; i < blen; i++)
@@ -96,12 +97,12 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
u8 *pos, *cpos, *b, *s_0, *a, *b_0;
b = scratch;
- s_0 = scratch + AES_BLOCK_LEN;
- a = scratch + 2 * AES_BLOCK_LEN;
- b_0 = scratch + 3 * AES_BLOCK_LEN;
+ s_0 = scratch + AES_BLOCK_SIZE;
+ a = scratch + 2 * AES_BLOCK_SIZE;
+ b_0 = scratch + 3 * AES_BLOCK_SIZE;
- num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
- last_len = data_len % AES_BLOCK_LEN;
+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
+ last_len = data_len % AES_BLOCK_SIZE;
aes_ccm_prepare(tfm, scratch, a);
/* Process payload blocks */
@@ -109,7 +110,7 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
pos = data;
for (j = 1; j <= num_blocks; j++) {
int blen = (j == num_blocks && last_len) ?
- last_len : AES_BLOCK_LEN;
+ last_len : AES_BLOCK_SIZE;
/* Decryption followed by authentication */
b_0[14] = (j >> 8) & 0xff;
diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h
index 6e7820ef3448..5b7d744e2370 100644
--- a/net/mac80211/aes_ccm.h
+++ b/net/mac80211/aes_ccm.h
@@ -12,8 +12,6 @@
#include <linux/crypto.h>
-#define AES_BLOCK_LEN 16
-
struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]);
void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
u8 *data, size_t data_len,
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c
index d502b2684a66..8dfd70d8fcfb 100644
--- a/net/mac80211/aes_cmac.c
+++ b/net/mac80211/aes_cmac.c
@@ -11,12 +11,12 @@
#include <linux/types.h>
#include <linux/crypto.h>
#include <linux/err.h>
+#include <crypto/aes.h>
#include <net/mac80211.h>
#include "key.h"
#include "aes_cmac.h"
-#define AES_BLOCK_SIZE 16
#define AES_CMAC_KEY_LEN 16
#define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */
#define AAD_LEN 20
@@ -35,10 +35,10 @@ static void gf_mulx(u8 *pad)
}
-static void aes_128_cmac_vector(struct crypto_cipher *tfm, u8 *scratch,
- size_t num_elem,
+static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
+ u8 scratch[2 * AES_BLOCK_SIZE];
u8 *cbc, *pad;
const u8 *pos, *end;
size_t i, e, left, total_len;
@@ -95,7 +95,7 @@ static void aes_128_cmac_vector(struct crypto_cipher *tfm, u8 *scratch,
}
-void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad,
+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
const u8 *data, size_t data_len, u8 *mic)
{
const u8 *addr[3];
@@ -110,7 +110,7 @@ void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad,
addr[2] = zero;
len[2] = CMAC_TLEN;
- aes_128_cmac_vector(tfm, scratch, 3, addr, len, mic);
+ aes_128_cmac_vector(tfm, 3, addr, len, mic);
}
diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h
index 0eb9a4831508..20785a647254 100644
--- a/net/mac80211/aes_cmac.h
+++ b/net/mac80211/aes_cmac.h
@@ -12,7 +12,7 @@
#include <linux/crypto.h>
struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]);
-void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad,
+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
const u8 *data, size_t data_len, u8 *mic);
void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm);
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 9c0d76cdca92..ebadb9ac9a7e 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -100,6 +100,21 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
mutex_unlock(&sta->ampdu_mlme.mtx);
}
+void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
+ const u8 *addr)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct sta_info *sta = sta_info_get(sdata, addr);
+ int i;
+
+ for (i = 0; i < STA_TID_NUM; i++)
+ if (ba_rx_bitmap & BIT(i))
+ set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested);
+
+ ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
+}
+EXPORT_SYMBOL(ieee80211_stop_rx_ba_session);
+
/*
* After accepting the AddBA Request we activated a timer,
* resetting it after each frame that arrives from the originator.
@@ -247,7 +262,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
"%pM on tid %u\n",
mgmt->sa, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto end;
+
+ /* delete existing Rx BA session on the same tid */
+ ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
+ WLAN_STATUS_UNSPECIFIED_QOS,
+ false);
}
/* prepare A-MPDU MLME for Rx aggregation */
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index be70c70d3f5b..bfc36e904764 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -209,6 +209,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
u8 seq[6] = {0};
struct key_params params;
struct ieee80211_key *key = NULL;
+ u64 pn64;
u32 iv32;
u16 iv16;
int err = -ENOENT;
@@ -256,22 +257,24 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
params.seq_len = 6;
break;
case WLAN_CIPHER_SUITE_CCMP:
- seq[0] = key->u.ccmp.tx_pn[5];
- seq[1] = key->u.ccmp.tx_pn[4];
- seq[2] = key->u.ccmp.tx_pn[3];
- seq[3] = key->u.ccmp.tx_pn[2];
- seq[4] = key->u.ccmp.tx_pn[1];
- seq[5] = key->u.ccmp.tx_pn[0];
+ pn64 = atomic64_read(&key->u.ccmp.tx_pn);
+ seq[0] = pn64;
+ seq[1] = pn64 >> 8;
+ seq[2] = pn64 >> 16;
+ seq[3] = pn64 >> 24;
+ seq[4] = pn64 >> 32;
+ seq[5] = pn64 >> 40;
params.seq = seq;
params.seq_len = 6;
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
- seq[0] = key->u.aes_cmac.tx_pn[5];
- seq[1] = key->u.aes_cmac.tx_pn[4];
- seq[2] = key->u.aes_cmac.tx_pn[3];
- seq[3] = key->u.aes_cmac.tx_pn[2];
- seq[4] = key->u.aes_cmac.tx_pn[1];
- seq[5] = key->u.aes_cmac.tx_pn[0];
+ pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
+ seq[0] = pn64;
+ seq[1] = pn64 >> 8;
+ seq[2] = pn64 >> 16;
+ seq[3] = pn64 >> 24;
+ seq[4] = pn64 >> 32;
+ seq[5] = pn64 >> 40;
params.seq = seq;
params.seq_len = 6;
break;
@@ -674,8 +677,11 @@ static void sta_apply_parameters(struct ieee80211_local *local,
if (mask & BIT(NL80211_STA_FLAG_WME)) {
sta->flags &= ~WLAN_STA_WME;
- if (set & BIT(NL80211_STA_FLAG_WME))
+ sta->sta.wme = false;
+ if (set & BIT(NL80211_STA_FLAG_WME)) {
sta->flags |= WLAN_STA_WME;
+ sta->sta.wme = true;
+ }
}
if (mask & BIT(NL80211_STA_FLAG_MFP)) {
@@ -1554,6 +1560,19 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
return local->ops->testmode_cmd(&local->hw, data, len);
}
+
+static int ieee80211_testmode_dump(struct wiphy *wiphy,
+ struct sk_buff *skb,
+ struct netlink_callback *cb,
+ void *data, int len)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ if (!local->ops->testmode_dump)
+ return -EOPNOTSUPP;
+
+ return local->ops->testmode_dump(&local->hw, skb, cb, data, len);
+}
#endif
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
@@ -2085,6 +2104,21 @@ static void ieee80211_get_ringparam(struct wiphy *wiphy,
drv_get_ringparam(local, tx, tx_max, rx, rx_max);
}
+static int ieee80211_set_rekey_data(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (!local->ops->set_rekey_data)
+ return -EOPNOTSUPP;
+
+ drv_set_rekey_data(local, sdata, data);
+
+ return 0;
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -2134,6 +2168,7 @@ struct cfg80211_ops mac80211_config_ops = {
.set_wds_peer = ieee80211_set_wds_peer,
.rfkill_poll = ieee80211_rfkill_poll,
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
+ CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
.set_power_mgmt = ieee80211_set_power_mgmt,
.set_bitrate_mask = ieee80211_set_bitrate_mask,
.remain_on_channel = ieee80211_remain_on_channel,
@@ -2146,4 +2181,5 @@ struct cfg80211_ops mac80211_config_ops = {
.get_antenna = ieee80211_get_antenna,
.set_ringparam = ieee80211_set_ringparam,
.get_ringparam = ieee80211_get_ringparam,
+ .set_rekey_data = ieee80211_set_rekey_data,
};
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 33c58b85c911..38e6101190d9 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -78,7 +78,7 @@ KEY_OPS(algorithm);
static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- const u8 *tpn;
+ u64 pn;
char buf[20];
int len;
struct ieee80211_key *key = file->private_data;
@@ -94,15 +94,16 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
key->u.tkip.tx.iv16);
break;
case WLAN_CIPHER_SUITE_CCMP:
- tpn = key->u.ccmp.tx_pn;
+ pn = atomic64_read(&key->u.ccmp.tx_pn);
len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
- tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]);
+ (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
+ (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
- tpn = key->u.aes_cmac.tx_pn;
+ pn = atomic64_read(&key->u.aes_cmac.tx_pn);
len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
- tpn[0], tpn[1], tpn[2], tpn[3], tpn[4],
- tpn[5]);
+ (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
+ (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
break;
default:
return 0;
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index eebf7a67daf7..edd2dd79c9be 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -218,6 +218,16 @@ static inline int drv_hw_scan(struct ieee80211_local *local,
return ret;
}
+static inline void drv_cancel_hw_scan(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ might_sleep();
+
+ trace_drv_cancel_hw_scan(local, sdata);
+ local->ops->cancel_hw_scan(&local->hw, &sdata->vif);
+ trace_drv_return_void(local);
+}
+
static inline int
drv_sched_scan_start(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -637,4 +647,14 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
return ret;
}
+static inline void drv_set_rekey_data(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ trace_drv_set_rekey_data(local, sdata, data);
+ if (local->ops->set_rekey_data)
+ local->ops->set_rekey_data(&local->hw, &sdata->vif, data);
+ trace_drv_return_void(local);
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index ed9edcbd9aa5..31a9dfa81f65 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -460,6 +460,12 @@ DEFINE_EVENT(local_sdata_evt, drv_hw_scan,
TP_ARGS(local, sdata)
);
+DEFINE_EVENT(local_sdata_evt, drv_cancel_hw_scan,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
+
DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata),
@@ -1018,6 +1024,34 @@ TRACE_EVENT(drv_set_bitrate_mask,
)
);
+TRACE_EVENT(drv_set_rekey_data,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_gtk_rekey_data *data),
+
+ TP_ARGS(local, sdata, data),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __array(u8, kek, NL80211_KEK_LEN)
+ __array(u8, kck, NL80211_KCK_LEN)
+ __array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ memcpy(__entry->kek, data->kek, NL80211_KEK_LEN);
+ memcpy(__entry->kck, data->kck, NL80211_KCK_LEN);
+ memcpy(__entry->replay_ctr, data->replay_ctr,
+ NL80211_REPLAY_CTR_LEN);
+ ),
+
+ TP_printk(LOCAL_PR_FMT VIF_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG)
+);
+
/*
* Tracing for API calls that drivers call.
*/
@@ -1287,6 +1321,27 @@ DEFINE_EVENT(local_only_evt, api_remain_on_channel_expired,
TP_ARGS(local)
);
+TRACE_EVENT(api_gtk_rekey_notify,
+ TP_PROTO(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const u8 *replay_ctr),
+
+ TP_ARGS(sdata, bssid, replay_ctr),
+
+ TP_STRUCT__entry(
+ VIF_ENTRY
+ __array(u8, bssid, ETH_ALEN)
+ __array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN)
+ ),
+
+ TP_fast_assign(
+ VIF_ASSIGN;
+ memcpy(__entry->bssid, bssid, ETH_ALEN);
+ memcpy(__entry->replay_ctr, replay_ctr, NL80211_REPLAY_CTR_LEN);
+ ),
+
+ TP_printk(VIF_PR_FMT, VIF_PR_ARG)
+);
+
/*
* Tracing for internal functions
* (which may also be called in response to driver calls)
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 591add22bcc0..7cfc286946c0 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -140,6 +140,12 @@ void ieee80211_ba_session_work(struct work_struct *work)
sta, tid, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_TIMEOUT, true);
+ if (test_and_clear_bit(tid,
+ sta->ampdu_mlme.tid_rx_stop_requested))
+ ___ieee80211_stop_rx_ba_session(
+ sta, tid, WLAN_BACK_RECIPIENT,
+ WLAN_REASON_UNSPECIFIED, true);
+
tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
if (tid_tx) {
/*
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 090b0ec1e056..4c7a831e7d1e 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -202,7 +202,22 @@ struct ieee80211_rx_data {
struct ieee80211_key *key;
unsigned int flags;
- int queue;
+
+ /*
+ * Index into sequence numbers array, 0..16
+ * since the last (16) is used for non-QoS,
+ * will be 16 on non-QoS frames.
+ */
+ int seqno_idx;
+
+ /*
+ * Index into the security IV/PN arrays, 0..16
+ * since the last (16) is used for CCMP-encrypted
+ * management frames, will be set to 16 on mgmt
+ * frames and 0 on non-QoS frames.
+ */
+ int security_idx;
+
u32 tkip_iv32;
u16 tkip_iv16;
};
@@ -544,6 +559,9 @@ struct ieee80211_sub_if_data {
/* keys */
struct list_head key_list;
+ /* count for keys needing tailroom space allocation */
+ int crypto_tx_tailroom_needed_cnt;
+
struct net_device *dev;
struct ieee80211_local *local;
@@ -1350,10 +1368,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
u8 *dst,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len);
+ const u8 *ie, size_t ie_len,
+ bool directed);
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len);
+ const u8 *ie, size_t ie_len,
+ bool directed);
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index f825e2f0a57e..739bee13e813 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -61,6 +61,36 @@ static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key)
return NULL;
}
+static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata)
+{
+ /*
+ * When this count is zero, SKB resizing for allocating tailroom
+ * for IV or MMIC is skipped. But, this check has created two race
+ * cases in xmit path while transiting from zero count to one:
+ *
+ * 1. SKB resize was skipped because no key was added but just before
+ * the xmit key is added and SW encryption kicks off.
+ *
+ * 2. SKB resize was skipped because all the keys were hw planted but
+ * just before xmit one of the key is deleted and SW encryption kicks
+ * off.
+ *
+ * In both the above case SW encryption will find not enough space for
+ * tailroom and exits with WARN_ON. (See WARN_ONs at wpa.c)
+ *
+ * Solution has been explained at
+ * http://mid.gmane.org/1308590980.4322.19.camel@jlt3.sipsolutions.net
+ */
+
+ if (!sdata->crypto_tx_tailroom_needed_cnt++) {
+ /*
+ * Flush all XMIT packets currently using HW encryption or no
+ * encryption at all if the count transition is from 0 -> 1.
+ */
+ synchronize_net();
+ }
+}
+
static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
{
struct ieee80211_sub_if_data *sdata;
@@ -101,6 +131,11 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
if (!ret) {
key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+ if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+ (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+ sdata->crypto_tx_tailroom_needed_cnt--;
+
return 0;
}
@@ -142,6 +177,10 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
sta = get_sta_for_key(key);
sdata = key->sdata;
+ if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+ (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+ increment_tailroom_need_count(sdata);
+
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
@@ -330,6 +369,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
get_unaligned_le16(seq);
}
}
+ spin_lock_init(&key->u.tkip.txlock);
break;
case WLAN_CIPHER_SUITE_CCMP:
key->conf.iv_len = CCMP_HDR_LEN;
@@ -394,8 +434,10 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
ieee80211_aes_key_free(key->u.ccmp.tfm);
if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
- if (key->local)
+ if (key->local) {
ieee80211_debugfs_key_remove(key);
+ key->sdata->crypto_tx_tailroom_needed_cnt--;
+ }
kfree(key);
}
@@ -452,6 +494,8 @@ int ieee80211_key_link(struct ieee80211_key *key,
else
old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
+ increment_tailroom_need_count(sdata);
+
__ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
__ieee80211_key_destroy(old_key);
@@ -498,12 +542,49 @@ void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
mutex_lock(&sdata->local->key_mtx);
- list_for_each_entry(key, &sdata->key_list, list)
+ sdata->crypto_tx_tailroom_needed_cnt = 0;
+
+ list_for_each_entry(key, &sdata->key_list, list) {
+ increment_tailroom_need_count(sdata);
ieee80211_key_enable_hw_accel(key);
+ }
mutex_unlock(&sdata->local->key_mtx);
}
+void ieee80211_iter_keys(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_key *key;
+ struct ieee80211_sub_if_data *sdata;
+
+ ASSERT_RTNL();
+
+ mutex_lock(&local->key_mtx);
+ if (vif) {
+ sdata = vif_to_sdata(vif);
+ list_for_each_entry(key, &sdata->key_list, list)
+ iter(hw, &sdata->vif,
+ key->sta ? &key->sta->sta : NULL,
+ &key->conf, iter_data);
+ } else {
+ list_for_each_entry(sdata, &local->interfaces, list)
+ list_for_each_entry(key, &sdata->key_list, list)
+ iter(hw, &sdata->vif,
+ key->sta ? &key->sta->sta : NULL,
+ &key->conf, iter_data);
+ }
+ mutex_unlock(&local->key_mtx);
+}
+EXPORT_SYMBOL(ieee80211_iter_keys);
+
void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_key *key;
@@ -533,3 +614,89 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&sdata->local->key_mtx);
}
+
+
+void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ trace_api_gtk_rekey_notify(sdata, bssid, replay_ctr);
+
+ cfg80211_gtk_rekey_notify(sdata->dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_notify);
+
+void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf,
+ struct ieee80211_key_seq *seq)
+{
+ struct ieee80211_key *key;
+ u64 pn64;
+
+ if (WARN_ON(!(keyconf->flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+ return;
+
+ key = container_of(keyconf, struct ieee80211_key, conf);
+
+ switch (key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ seq->tkip.iv32 = key->u.tkip.tx.iv32;
+ seq->tkip.iv16 = key->u.tkip.tx.iv16;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ pn64 = atomic64_read(&key->u.ccmp.tx_pn);
+ seq->ccmp.pn[5] = pn64;
+ seq->ccmp.pn[4] = pn64 >> 8;
+ seq->ccmp.pn[3] = pn64 >> 16;
+ seq->ccmp.pn[2] = pn64 >> 24;
+ seq->ccmp.pn[1] = pn64 >> 32;
+ seq->ccmp.pn[0] = pn64 >> 40;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
+ seq->ccmp.pn[5] = pn64;
+ seq->ccmp.pn[4] = pn64 >> 8;
+ seq->ccmp.pn[3] = pn64 >> 16;
+ seq->ccmp.pn[2] = pn64 >> 24;
+ seq->ccmp.pn[1] = pn64 >> 32;
+ seq->ccmp.pn[0] = pn64 >> 40;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+EXPORT_SYMBOL(ieee80211_get_key_tx_seq);
+
+void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
+ int tid, struct ieee80211_key_seq *seq)
+{
+ struct ieee80211_key *key;
+ const u8 *pn;
+
+ key = container_of(keyconf, struct ieee80211_key, conf);
+
+ switch (key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (WARN_ON(tid < 0 || tid >= NUM_RX_DATA_QUEUES))
+ return;
+ seq->tkip.iv32 = key->u.tkip.rx[tid].iv32;
+ seq->tkip.iv16 = key->u.tkip.rx[tid].iv16;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (WARN_ON(tid < -1 || tid >= NUM_RX_DATA_QUEUES))
+ return;
+ if (tid < 0)
+ pn = key->u.ccmp.rx_pn[NUM_RX_DATA_QUEUES];
+ else
+ pn = key->u.ccmp.rx_pn[tid];
+ memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN);
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ if (WARN_ON(tid != 0))
+ return;
+ pn = key->u.aes_cmac.rx_pn;
+ memcpy(seq->aes_cmac.pn, pn, CMAC_PN_LEN);
+ break;
+ }
+}
+EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index d801d5351336..86b216b01415 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -28,8 +28,9 @@
#define CCMP_PN_LEN 6
#define TKIP_IV_LEN 8
#define TKIP_ICV_LEN 4
+#define CMAC_PN_LEN 6
-#define NUM_RX_DATA_QUEUES 17
+#define NUM_RX_DATA_QUEUES 16
struct ieee80211_local;
struct ieee80211_sub_if_data;
@@ -52,9 +53,10 @@ enum ieee80211_internal_tkip_state {
};
struct tkip_ctx {
- u32 iv32;
- u16 iv16;
- u16 p1k[5];
+ u32 iv32; /* current iv32 */
+ u16 iv16; /* current iv16 */
+ u16 p1k[5]; /* p1k cache */
+ u32 p1k_iv32; /* iv32 for which p1k computed */
enum ieee80211_internal_tkip_state state;
};
@@ -71,6 +73,9 @@ struct ieee80211_key {
union {
struct {
+ /* protects tx context */
+ spinlock_t txlock;
+
/* last used TSC */
struct tkip_ctx tx;
@@ -78,32 +83,23 @@ struct ieee80211_key {
struct tkip_ctx rx[NUM_RX_DATA_QUEUES];
} tkip;
struct {
- u8 tx_pn[6];
+ atomic64_t tx_pn;
/*
* Last received packet number. The first
* NUM_RX_DATA_QUEUES counters are used with Data
* frames and the last counter is used with Robust
* Management frames.
*/
- u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6];
+ u8 rx_pn[NUM_RX_DATA_QUEUES + 1][CCMP_PN_LEN];
struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCCMPReplays */
- /* scratch buffers for virt_to_page() (crypto API) */
-#ifndef AES_BLOCK_LEN
-#define AES_BLOCK_LEN 16
-#endif
- u8 tx_crypto_buf[6 * AES_BLOCK_LEN];
- u8 rx_crypto_buf[6 * AES_BLOCK_LEN];
} ccmp;
struct {
- u8 tx_pn[6];
- u8 rx_pn[6];
+ atomic64_t tx_pn;
+ u8 rx_pn[CMAC_PN_LEN];
struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCMACReplays */
u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
- /* scratch buffers for virt_to_page() (crypto API) */
- u8 tx_crypto_buf[2 * AES_BLOCK_LEN];
- u8 rx_crypto_buf[2 * AES_BLOCK_LEN];
} aes_cmac;
} u;
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 0d2faacc3e87..068ee6518254 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -647,12 +647,12 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
mpath = node->mpath;
if (mpath->sdata == sdata &&
memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
- spin_lock_bh(&mpath->state_lock);
+ spin_lock(&mpath->state_lock);
mpath->flags |= MESH_PATH_RESOLVING;
hlist_del_rcu(&node->list);
call_rcu(&node->rcu, mesh_path_node_reclaim);
atomic_dec(&tbl->entries);
- spin_unlock_bh(&mpath->state_lock);
+ spin_unlock(&mpath->state_lock);
goto enddel;
}
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d595265d6c22..b6d9bd5f4d3c 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -749,7 +749,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
container_of(work, struct ieee80211_local,
dynamic_ps_enable_work);
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_if_managed *ifmgd;
unsigned long flags;
int q;
@@ -757,26 +757,39 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (!sdata)
return;
+ ifmgd = &sdata->u.mgd;
+
if (local->hw.conf.flags & IEEE80211_CONF_PS)
return;
- /*
- * transmission can be stopped by others which leads to
- * dynamic_ps_timer expiry. Postpond the ps timer if it
- * is not the actual idle state.
- */
- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- for (q = 0; q < local->hw.queues; q++) {
- if (local->queue_stop_reasons[q]) {
- spin_unlock_irqrestore(&local->queue_stop_reason_lock,
- flags);
+ if (!local->disable_dynamic_ps &&
+ local->hw.conf.dynamic_ps_timeout > 0) {
+ /* don't enter PS if TX frames are pending */
+ if (drv_tx_frames_pending(local)) {
mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies(
local->hw.conf.dynamic_ps_timeout));
return;
}
+
+ /*
+ * transmission can be stopped by others which leads to
+ * dynamic_ps_timer expiry. Postpone the ps timer if it
+ * is not the actual idle state.
+ */
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ for (q = 0; q < local->hw.queues; q++) {
+ if (local->queue_stop_reasons[q]) {
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+ flags);
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(
+ local->hw.conf.dynamic_ps_timeout));
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}
- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
(!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) {
@@ -801,7 +814,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
- netif_tx_wake_all_queues(sdata->dev);
+ if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+ netif_tx_wake_all_queues(sdata->dev);
}
void ieee80211_dynamic_ps_timer(unsigned long data)
@@ -1204,7 +1218,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ieee80211_send_nullfunc(sdata->local, sdata, 0);
} else {
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
- ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0);
+ ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0,
+ true);
}
ifmgd->probe_send_count++;
@@ -1289,7 +1304,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
- ssid + 2, ssid[1], NULL, 0);
+ ssid + 2, ssid[1], NULL, 0, true);
return skb;
}
@@ -2200,12 +2215,16 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ if (!ifmgd->associated)
+ return;
+
if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
add_timer(&ifmgd->timer);
if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
add_timer(&ifmgd->chswitch_timer);
ieee80211_sta_reset_beacon_monitor(sdata);
ieee80211_restart_sta_timer(sdata);
+ ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.monitor_work);
}
#endif
@@ -2652,3 +2671,10 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
}
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
+
+unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ return sdata->dev->operstate;
+}
+EXPORT_SYMBOL(ieee80211_get_operstate);
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 730778a2c90c..f87e993e713b 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -6,6 +6,28 @@
#include "driver-ops.h"
#include "led.h"
+/* return value indicates whether the driver should be further notified */
+static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ ieee80211_sta_quiesce(sdata);
+ return true;
+ case NL80211_IFTYPE_ADHOC:
+ ieee80211_ibss_quiesce(sdata);
+ return true;
+ case NL80211_IFTYPE_MESH_POINT:
+ ieee80211_mesh_quiesce(sdata);
+ return true;
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ /* don't tell driver about this */
+ return false;
+ default:
+ return true;
+ }
+}
+
int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -50,11 +72,19 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
local->wowlan = wowlan && local->open_count;
if (local->wowlan) {
int err = drv_suspend(local, wowlan);
- if (err) {
+ if (err < 0) {
local->quiescing = false;
return err;
+ } else if (err > 0) {
+ WARN_ON(err != 1);
+ local->wowlan = false;
+ } else {
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ cancel_work_sync(&sdata->work);
+ ieee80211_quiesce(sdata);
+ }
+ goto suspend;
}
- goto suspend;
}
/* disable keys */
@@ -82,23 +112,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
list_for_each_entry(sdata, &local->interfaces, list) {
cancel_work_sync(&sdata->work);
- switch(sdata->vif.type) {
- case NL80211_IFTYPE_STATION:
- ieee80211_sta_quiesce(sdata);
- break;
- case NL80211_IFTYPE_ADHOC:
- ieee80211_ibss_quiesce(sdata);
- break;
- case NL80211_IFTYPE_MESH_POINT:
- ieee80211_mesh_quiesce(sdata);
- break;
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_MONITOR:
- /* don't tell driver about this */
+ if (!ieee80211_quiesce(sdata))
continue;
- default:
- break;
- }
if (!ieee80211_sdata_running(sdata))
continue;
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 8adac67395f7..58a89554b788 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -532,12 +532,21 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
mp->hw = hw;
mp->update_interval = 100;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ mp->fixed_rate_idx = (u32) -1;
+ mp->dbg_fixed_rate = debugfs_create_u32("fixed_rate_idx",
+ S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx);
+#endif
+
return mp;
}
static void
minstrel_free(void *priv)
{
+#ifdef CONFIG_MAC80211_DEBUGFS
+ debugfs_remove(((struct minstrel_priv *)priv)->dbg_fixed_rate);
+#endif
kfree(priv);
}
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 0f5a83370aa6..5d278eccaef0 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -78,6 +78,18 @@ struct minstrel_priv {
unsigned int update_interval;
unsigned int lookaround_rate;
unsigned int lookaround_rate_mrr;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ /*
+ * enable fixed rate processing per RC
+ * - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx
+ * - write -1 to enable RC processing again
+ * - setting will be applied on next update
+ */
+ u32 fixed_rate_idx;
+ struct dentry *dbg_fixed_rate;
+#endif
+
};
struct minstrel_debugfs_info {
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 333b5118be6d..66a1eeb279c6 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -609,6 +609,13 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
info->flags |= mi->tx_flags;
sample_idx = minstrel_get_sample_rate(mp, mi);
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ /* use fixed index if set */
+ if (mp->fixed_rate_idx != -1)
+ sample_idx = mp->fixed_rate_idx;
+#endif
+
if (sample_idx >= 0) {
sample = true;
minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 7fa8c6be7bf0..e6dccc70931d 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -331,15 +331,18 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
- int tid;
+ int tid, seqno_idx, security_idx;
/* does the frame have a qos control field? */
if (ieee80211_is_data_qos(hdr->frame_control)) {
u8 *qc = ieee80211_get_qos_ctl(hdr);
/* frame has qos control */
tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
- if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+ if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
status->rx_flags |= IEEE80211_RX_AMSDU;
+
+ seqno_idx = tid;
+ security_idx = tid;
} else {
/*
* IEEE 802.11-2007, 7.1.3.4.1 ("Sequence Number field"):
@@ -352,10 +355,15 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
*
* We also use that counter for non-QoS STAs.
*/
- tid = NUM_RX_DATA_QUEUES - 1;
+ seqno_idx = NUM_RX_DATA_QUEUES;
+ security_idx = 0;
+ if (ieee80211_is_mgmt(hdr->frame_control))
+ security_idx = NUM_RX_DATA_QUEUES;
+ tid = 0;
}
- rx->queue = tid;
+ rx->seqno_idx = seqno_idx;
+ rx->security_idx = security_idx;
/* Set skb->priority to 1d tag if highest order bit of TID is not set.
* For now, set skb->priority to 0 for other cases. */
rx->skb->priority = (tid > 7) ? 0 : tid;
@@ -810,7 +818,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
- rx->sta->last_seq_ctrl[rx->queue] ==
+ rx->sta->last_seq_ctrl[rx->seqno_idx] ==
hdr->seq_ctrl)) {
if (status->rx_flags & IEEE80211_RX_RA_MATCH) {
rx->local->dot11FrameDuplicateCount++;
@@ -818,7 +826,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
}
return RX_DROP_UNUSABLE;
} else
- rx->sta->last_seq_ctrl[rx->queue] = hdr->seq_ctrl;
+ rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
}
if (unlikely(rx->skb->len < 16)) {
@@ -1374,11 +1382,10 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
if (frag == 0) {
/* This is the first fragment of a new frame. */
entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
- rx->queue, &(rx->skb));
+ rx->seqno_idx, &(rx->skb));
if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP &&
ieee80211_has_protected(fc)) {
- int queue = ieee80211_is_mgmt(fc) ?
- NUM_RX_DATA_QUEUES : rx->queue;
+ int queue = rx->security_idx;
/* Store CCMP PN so that we can verify that the next
* fragment has a sequential PN value. */
entry->ccmp = 1;
@@ -1392,7 +1399,8 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
/* This is a fragment for a frame that should already be pending in
* fragment cache. Add this fragment to the end of the pending entry.
*/
- entry = ieee80211_reassemble_find(rx->sdata, frag, seq, rx->queue, hdr);
+ entry = ieee80211_reassemble_find(rx->sdata, frag, seq,
+ rx->seqno_idx, hdr);
if (!entry) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
return RX_DROP_MONITOR;
@@ -1412,8 +1420,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
if (pn[i])
break;
}
- queue = ieee80211_is_mgmt(fc) ?
- NUM_RX_DATA_QUEUES : rx->queue;
+ queue = rx->security_idx;
rpn = rx->key->u.ccmp.rx_pn[queue];
if (memcmp(pn, rpn, CCMP_PN_LEN))
return RX_DROP_UNUSABLE;
@@ -2590,7 +2597,9 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
.sta = sta,
.sdata = sta->sdata,
.local = sta->local,
- .queue = tid,
+ /* This is OK -- must be QoS data frame */
+ .security_idx = tid,
+ .seqno_idx = tid,
.flags = 0,
};
struct tid_ampdu_rx *tid_agg_rx;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 669d2e32efb6..08a45ac3d6f8 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -228,6 +228,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
{
struct cfg80211_scan_request *req = local->scan_req;
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
enum ieee80211_band band;
int i, ielen, n_chans;
@@ -251,8 +252,8 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
local->hw_scan_req->n_channels = n_chans;
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
- req->ie, req->ie_len, band, (u32) -1,
- 0);
+ req->ie, req->ie_len, band,
+ sdata->rc_rateidx_mask[band], 0);
local->hw_scan_req->ie_len = ielen;
return true;
@@ -658,7 +659,8 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
sdata, NULL,
local->scan_req->ssids[i].ssid,
local->scan_req->ssids[i].ssid_len,
- local->scan_req->ie, local->scan_req->ie_len);
+ local->scan_req->ie, local->scan_req->ie_len,
+ false);
/*
* After sending probe requests, wait for probe responses
@@ -821,10 +823,8 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
*/
void ieee80211_scan_cancel(struct ieee80211_local *local)
{
- bool abortscan;
-
/*
- * We are only canceling software scan, or deferred scan that was not
+ * We are canceling software scan, or deferred scan that was not
* yet really started (see __ieee80211_start_scan ).
*
* Regarding hardware scan:
@@ -836,23 +836,30 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
* - we can not cancel scan_work since driver can schedule it
* by ieee80211_scan_completed(..., true) to finish scan
*
- * Hence low lever driver is responsible for canceling HW scan.
+ * Hence we only call the cancel_hw_scan() callback, but the low-level
+ * driver is still responsible for calling ieee80211_scan_completed()
+ * after the scan was completed/aborted.
*/
mutex_lock(&local->mtx);
- abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning);
- if (abortscan) {
- /*
- * The scan is canceled, but stop work from being pending.
- *
- * If the work is currently running, it must be blocked on
- * the mutex, but we'll set scan_sdata = NULL and it'll
- * simply exit once it acquires the mutex.
- */
- cancel_delayed_work(&local->scan_work);
- /* and clean up */
- __ieee80211_scan_completed(&local->hw, true, false);
+ if (!local->scan_req)
+ goto out;
+
+ if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
+ if (local->ops->cancel_hw_scan)
+ drv_cancel_hw_scan(local, local->scan_sdata);
+ goto out;
}
+
+ /*
+ * If the work is currently running, it must be blocked on
+ * the mutex, but we'll set scan_sdata = NULL and it'll
+ * simply exit once it acquires the mutex.
+ */
+ cancel_delayed_work(&local->scan_work);
+ /* and clean up */
+ __ieee80211_scan_completed(&local->hw, true, false);
+out:
mutex_unlock(&local->mtx);
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index c6ae8718bd57..28beb78e601e 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -158,6 +158,8 @@ struct tid_ampdu_rx {
* @work: work struct for starting/stopping aggregation
* @tid_rx_timer_expired: bitmap indicating on which TIDs the
* RX timer expired until the work for it runs
+ * @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the
+ * driver requested to close until the work for it runs
* @mtx: mutex to protect all TX data (except non-NULL assignments
* to tid_tx[idx], which are protected by the sta spinlock)
*/
@@ -166,6 +168,7 @@ struct sta_ampdu_mlme {
/* rx */
struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM];
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)];
+ unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)];
/* tx */
struct work_struct work;
struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM];
@@ -284,7 +287,8 @@ struct sta_info {
unsigned long rx_dropped;
int last_signal;
struct ewma avg_signal;
- __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
+ /* Plus 1 for non-QoS frames */
+ __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1];
/* Updated from TX status path only, no locking requirements */
unsigned long tx_filtered_count;
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 757e4eb2baf7..cc79e697cdb2 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -101,6 +101,7 @@ static void tkip_mixing_phase1(const u8 *tk, struct tkip_ctx *ctx,
p1k[4] += tkipS(p1k[3] ^ get_unaligned_le16(tk + 0 + j)) + i;
}
ctx->state = TKIP_STATE_PHASE1_DONE;
+ ctx->p1k_iv32 = tsc_IV32;
}
static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx,
@@ -140,60 +141,69 @@ static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx,
/* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets
* of the IV. Returns pointer to the octet following IVs (i.e., beginning of
* the packet payload). */
-u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16)
+u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key)
{
- pos = write_tkip_iv(pos, iv16);
+ lockdep_assert_held(&key->u.tkip.txlock);
+
+ pos = write_tkip_iv(pos, key->u.tkip.tx.iv16);
*pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */;
put_unaligned_le32(key->u.tkip.tx.iv32, pos);
return pos + 4;
}
-void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf,
- struct sk_buff *skb, enum ieee80211_tkip_key_type type,
- u8 *outkey)
+static void ieee80211_compute_tkip_p1k(struct ieee80211_key *key, u32 iv32)
{
- struct ieee80211_key *key = (struct ieee80211_key *)
- container_of(keyconf, struct ieee80211_key, conf);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- u8 *data;
- const u8 *tk;
- struct tkip_ctx *ctx;
- u16 iv16;
- u32 iv32;
-
- data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
- iv16 = data[2] | (data[0] << 8);
- iv32 = get_unaligned_le32(&data[4]);
-
- tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
- ctx = &key->u.tkip.tx;
+ struct ieee80211_sub_if_data *sdata = key->sdata;
+ struct tkip_ctx *ctx = &key->u.tkip.tx;
+ const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
-#ifdef CONFIG_MAC80211_TKIP_DEBUG
- printk(KERN_DEBUG "TKIP encrypt: iv16 = 0x%04x, iv32 = 0x%08x\n",
- iv16, iv32);
-
- if (iv32 != ctx->iv32) {
- printk(KERN_DEBUG "skb: iv32 = 0x%08x key: iv32 = 0x%08x\n",
- iv32, ctx->iv32);
- printk(KERN_DEBUG "Wrap around of iv16 in the middle of a "
- "fragmented packet\n");
- }
-#endif
+ lockdep_assert_held(&key->u.tkip.txlock);
+
+ /*
+ * Update the P1K when the IV32 is different from the value it
+ * had when we last computed it (or when not initialised yet).
+ * This might flip-flop back and forth if packets are processed
+ * out-of-order due to the different ACs, but then we have to
+ * just compute the P1K more often.
+ */
+ if (ctx->p1k_iv32 != iv32 || ctx->state == TKIP_STATE_NOT_INIT)
+ tkip_mixing_phase1(tk, ctx, sdata->vif.addr, iv32);
+}
- /* Update the p1k only when the iv16 in the packet wraps around, this
- * might occur after the wrap around of iv16 in the key in case of
- * fragmented packets. */
- if (iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT)
- tkip_mixing_phase1(tk, ctx, hdr->addr2, iv32);
+void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf,
+ u32 iv32, u16 *p1k)
+{
+ struct ieee80211_key *key = (struct ieee80211_key *)
+ container_of(keyconf, struct ieee80211_key, conf);
+ struct tkip_ctx *ctx = &key->u.tkip.tx;
+ unsigned long flags;
- if (type == IEEE80211_TKIP_P1_KEY) {
- memcpy(outkey, ctx->p1k, sizeof(u16) * 5);
- return;
- }
+ spin_lock_irqsave(&key->u.tkip.txlock, flags);
+ ieee80211_compute_tkip_p1k(key, iv32);
+ memcpy(p1k, ctx->p1k, sizeof(ctx->p1k));
+ spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
+}
+EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv);
- tkip_mixing_phase2(tk, ctx, iv16, outkey);
+void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
+ struct sk_buff *skb, u8 *p2k)
+{
+ struct ieee80211_key *key = (struct ieee80211_key *)
+ container_of(keyconf, struct ieee80211_key, conf);
+ const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
+ struct tkip_ctx *ctx = &key->u.tkip.tx;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+ u32 iv32 = get_unaligned_le32(&data[4]);
+ u16 iv16 = data[2] | (data[0] << 8);
+ unsigned long flags;
+
+ spin_lock_irqsave(&key->u.tkip.txlock, flags);
+ ieee80211_compute_tkip_p1k(key, iv32);
+ tkip_mixing_phase2(tk, ctx, iv16, p2k);
+ spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
}
-EXPORT_SYMBOL(ieee80211_get_tkip_key);
+EXPORT_SYMBOL(ieee80211_get_tkip_p2k);
/*
* Encrypt packet payload with TKIP using @key. @pos is a pointer to the
@@ -204,19 +214,15 @@ EXPORT_SYMBOL(ieee80211_get_tkip_key);
*/
int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm,
struct ieee80211_key *key,
- u8 *pos, size_t payload_len, u8 *ta)
+ struct sk_buff *skb,
+ u8 *payload, size_t payload_len)
{
u8 rc4key[16];
- struct tkip_ctx *ctx = &key->u.tkip.tx;
- const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
-
- /* Calculate per-packet key */
- if (ctx->iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT)
- tkip_mixing_phase1(tk, ctx, ta, ctx->iv32);
- tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key);
+ ieee80211_get_tkip_p2k(&key->conf, skb, rc4key);
- return ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, payload_len);
+ return ieee80211_wep_encrypt_data(tfm, rc4key, 16,
+ payload, payload_len);
}
/* Decrypt packet payload with TKIP using @key. @pos is a pointer to the
diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h
index 1cab9c86978f..e3ecb659b90a 100644
--- a/net/mac80211/tkip.h
+++ b/net/mac80211/tkip.h
@@ -13,11 +13,13 @@
#include <linux/crypto.h>
#include "key.h"
-u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16);
+u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key);
int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm,
- struct ieee80211_key *key,
- u8 *pos, size_t payload_len, u8 *ta);
+ struct ieee80211_key *key,
+ struct sk_buff *skb,
+ u8 *payload, size_t payload_len);
+
enum {
TKIP_DECRYPT_OK = 0,
TKIP_DECRYPT_NO_EXT_IV = -1,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 3104c844b544..e8d0d2d22665 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1474,18 +1474,14 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
/* device xmit handlers */
-static int ieee80211_skb_resize(struct ieee80211_local *local,
+static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
int head_need, bool may_encrypt)
{
+ struct ieee80211_local *local = sdata->local;
int tail_need = 0;
- /*
- * This could be optimised, devices that do full hardware
- * crypto (including TKIP MMIC) need no tailroom... But we
- * have no drivers for such devices currently.
- */
- if (may_encrypt) {
+ if (may_encrypt && sdata->crypto_tx_tailroom_needed_cnt) {
tail_need = IEEE80211_ENCRYPT_TAILROOM;
tail_need -= skb_tailroom(skb);
tail_need = max_t(int, tail_need, 0);
@@ -1578,7 +1574,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
headroom -= skb_headroom(skb);
headroom = max_t(int, 0, headroom);
- if (ieee80211_skb_resize(local, skb, headroom, may_encrypt)) {
+ if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) {
dev_kfree_skb(skb);
rcu_read_unlock();
return;
@@ -1945,7 +1941,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
head_need += IEEE80211_ENCRYPT_HEADROOM;
head_need += local->tx_headroom;
head_need = max_t(int, 0, head_need);
- if (ieee80211_skb_resize(local, skb, head_need, true))
+ if (ieee80211_skb_resize(sdata, skb, head_need, true))
goto fail;
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index d3fe2d237485..652e5695225a 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1018,7 +1018,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
u8 *dst,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len)
+ const u8 *ie, size_t ie_len,
+ bool directed)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
@@ -1035,8 +1036,16 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
return NULL;
}
- chan = ieee80211_frequency_to_channel(
- local->hw.conf.channel->center_freq);
+ /*
+ * Do not send DS Channel parameter for directed probe requests
+ * in order to maximize the chance that we get a response. Some
+ * badly-behaved APs don't respond when this parameter is included.
+ */
+ if (directed)
+ chan = 0;
+ else
+ chan = ieee80211_frequency_to_channel(
+ local->hw.conf.channel->center_freq);
buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len,
local->hw.conf.channel->band,
@@ -1062,11 +1071,13 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len)
+ const u8 *ie, size_t ie_len,
+ bool directed)
{
struct sk_buff *skb;
- skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len);
+ skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len,
+ directed);
if (skb)
ieee80211_tx_skb(sdata, skb);
}
@@ -1276,7 +1287,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (ieee80211_sdata_running(sdata))
ieee80211_enable_keys(sdata);
+#ifdef CONFIG_PM
wake_up:
+#endif
ieee80211_wake_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 28bc084dbfb9..7a49532f14cb 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -151,8 +151,7 @@ void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb)
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
if (unlikely(local->wifi_wme_noack_test))
- ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK <<
- QOS_CONTROL_ACK_POLICY_SHIFT;
+ ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
/* qos header is 2 bytes, second reserved */
*p++ = ack_policy | tid;
*p = 0;
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index 6053b1c9feee..faead6d02026 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -13,11 +13,6 @@
#include <linux/netdevice.h>
#include "ieee80211_i.h"
-#define QOS_CONTROL_ACK_POLICY_NORMAL 0
-#define QOS_CONTROL_ACK_POLICY_NOACK 1
-
-#define QOS_CONTROL_ACK_POLICY_SHIFT 5
-
extern const int ieee802_1d_to_ac[8];
u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index d2e7f0e86677..edf8583280c9 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -450,7 +450,7 @@ ieee80211_direct_probe(struct ieee80211_work *wk)
* will not answer to direct packet in unassociated state.
*/
ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid,
- wk->probe_auth.ssid_len, NULL, 0);
+ wk->probe_auth.ssid_len, NULL, 0, true);
wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
run_again(local, wk->timeout);
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 8f6a302d2ac3..7bc8702808fa 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -15,6 +15,7 @@
#include <linux/gfp.h>
#include <asm/unaligned.h>
#include <net/mac80211.h>
+#include <crypto/aes.h>
#include "ieee80211_i.h"
#include "michael.h"
@@ -86,11 +87,6 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- int queue = rx->queue;
-
- /* otherwise, TKIP is vulnerable to TID 0 vs. non-QoS replays */
- if (rx->queue == NUM_RX_DATA_QUEUES - 1)
- queue = 0;
/*
* it makes no sense to check for MIC errors on anything other
@@ -153,8 +149,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
update_iv:
/* update IV in key information to be able to detect replays */
- rx->key->u.tkip.rx[queue].iv32 = rx->tkip_iv32;
- rx->key->u.tkip.rx[queue].iv16 = rx->tkip_iv16;
+ rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32;
+ rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16;
return RX_CONTINUE;
@@ -176,6 +172,7 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_key *key = tx->key;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ unsigned long flags;
unsigned int hdrlen;
int len, tail;
u8 *pos;
@@ -203,11 +200,12 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
pos += hdrlen;
/* Increase IV for the frame */
+ spin_lock_irqsave(&key->u.tkip.txlock, flags);
key->u.tkip.tx.iv16++;
if (key->u.tkip.tx.iv16 == 0)
key->u.tkip.tx.iv32++;
-
- pos = ieee80211_tkip_add_iv(pos, key, key->u.tkip.tx.iv16);
+ pos = ieee80211_tkip_add_iv(pos, key);
+ spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
/* hwaccel - with software IV */
if (info->control.hw_key)
@@ -216,9 +214,8 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
/* Add room for ICV */
skb_put(skb, TKIP_ICV_LEN);
- hdr = (struct ieee80211_hdr *) skb->data;
return ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm,
- key, pos, len, hdr->addr2);
+ key, skb, pos, len);
}
@@ -246,11 +243,6 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- int queue = rx->queue;
-
- /* otherwise, TKIP is vulnerable to TID 0 vs. non-QoS replays */
- if (rx->queue == NUM_RX_DATA_QUEUES - 1)
- queue = 0;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -271,7 +263,7 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
res = ieee80211_tkip_decrypt_data(rx->local->wep_rx_tfm,
key, skb->data + hdrlen,
skb->len - hdrlen, rx->sta->sta.addr,
- hdr->addr1, hwaccel, queue,
+ hdr->addr1, hwaccel, rx->security_idx,
&rx->tkip_iv32,
&rx->tkip_iv16);
if (res != TKIP_DECRYPT_OK)
@@ -299,8 +291,10 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
unsigned int hdrlen;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- b_0 = scratch + 3 * AES_BLOCK_LEN;
- aad = scratch + 4 * AES_BLOCK_LEN;
+ memset(scratch, 0, 6 * AES_BLOCK_SIZE);
+
+ b_0 = scratch + 3 * AES_BLOCK_SIZE;
+ aad = scratch + 4 * AES_BLOCK_SIZE;
/*
* Mask FC: zero subtype b4 b5 b6 (if not mgmt)
@@ -389,8 +383,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
struct ieee80211_key *key = tx->key;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int hdrlen, len, tail;
- u8 *pos, *pn;
- int i;
+ u8 *pos;
+ u8 pn[6];
+ u64 pn64;
+ u8 scratch[6 * AES_BLOCK_SIZE];
if (info->control.hw_key &&
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
@@ -418,14 +414,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
hdr = (struct ieee80211_hdr *) pos;
pos += hdrlen;
- /* PN = PN + 1 */
- pn = key->u.ccmp.tx_pn;
+ pn64 = atomic64_inc_return(&key->u.ccmp.tx_pn);
- for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
- pn[i]++;
- if (pn[i])
- break;
- }
+ pn[5] = pn64;
+ pn[4] = pn64 >> 8;
+ pn[3] = pn64 >> 16;
+ pn[2] = pn64 >> 24;
+ pn[1] = pn64 >> 32;
+ pn[0] = pn64 >> 40;
ccmp_pn2hdr(pos, pn, key->conf.keyidx);
@@ -434,8 +430,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
return 0;
pos += CCMP_HDR_LEN;
- ccmp_special_blocks(skb, pn, key->u.ccmp.tx_crypto_buf, 0);
- ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, key->u.ccmp.tx_crypto_buf, pos, len,
+ ccmp_special_blocks(skb, pn, scratch, 0);
+ ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
pos, skb_put(skb, CCMP_MIC_LEN));
return 0;
@@ -482,8 +478,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
ccmp_hdr2pn(pn, skb->data + hdrlen);
- queue = ieee80211_is_mgmt(hdr->frame_control) ?
- NUM_RX_DATA_QUEUES : rx->queue;
+ queue = rx->security_idx;
if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) {
key->u.ccmp.replays++;
@@ -491,11 +486,12 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
}
if (!(status->flag & RX_FLAG_DECRYPTED)) {
+ u8 scratch[6 * AES_BLOCK_SIZE];
/* hardware didn't decrypt/verify MIC */
- ccmp_special_blocks(skb, pn, key->u.ccmp.rx_crypto_buf, 1);
+ ccmp_special_blocks(skb, pn, scratch, 1);
if (ieee80211_aes_ccm_decrypt(
- key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf,
+ key->u.ccmp.tfm, scratch,
skb->data + hdrlen + CCMP_HDR_LEN, data_len,
skb->data + skb->len - CCMP_MIC_LEN,
skb->data + hdrlen + CCMP_HDR_LEN))
@@ -526,6 +522,16 @@ static void bip_aad(struct sk_buff *skb, u8 *aad)
}
+static inline void bip_ipn_set64(u8 *d, u64 pn)
+{
+ *d++ = pn;
+ *d++ = pn >> 8;
+ *d++ = pn >> 16;
+ *d++ = pn >> 24;
+ *d++ = pn >> 32;
+ *d = pn >> 40;
+}
+
static inline void bip_ipn_swap(u8 *d, const u8 *s)
{
*d++ = s[5];
@@ -544,8 +550,8 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_key *key = tx->key;
struct ieee80211_mmie *mmie;
- u8 *pn, aad[20];
- int i;
+ u8 aad[20];
+ u64 pn64;
if (info->control.hw_key)
return 0;
@@ -559,22 +565,17 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)
mmie->key_id = cpu_to_le16(key->conf.keyidx);
/* PN = PN + 1 */
- pn = key->u.aes_cmac.tx_pn;
+ pn64 = atomic64_inc_return(&key->u.aes_cmac.tx_pn);
- for (i = sizeof(key->u.aes_cmac.tx_pn) - 1; i >= 0; i--) {
- pn[i]++;
- if (pn[i])
- break;
- }
- bip_ipn_swap(mmie->sequence_number, pn);
+ bip_ipn_set64(mmie->sequence_number, pn64);
bip_aad(skb, aad);
/*
* MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64)
*/
- ieee80211_aes_cmac(key->u.aes_cmac.tfm, key->u.aes_cmac.tx_crypto_buf,
- aad, skb->data + 24, skb->len - 24, mmie->mic);
+ ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad,
+ skb->data + 24, skb->len - 24, mmie->mic);
return TX_CONTINUE;
}
@@ -612,8 +613,7 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
if (!(status->flag & RX_FLAG_DECRYPTED)) {
/* hardware didn't decrypt/verify MIC */
bip_aad(skb, aad);
- ieee80211_aes_cmac(key->u.aes_cmac.tfm,
- key->u.aes_cmac.rx_crypto_buf, aad,
+ ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad,
skb->data + 24, skb->len - 24, mic);
if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {
key->u.aes_cmac.icverrors++;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 6ef64adf7362..a7ec8512f552 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1693,6 +1693,8 @@ static int netlink_dump(struct sock *sk)
if (!nlh)
goto errout_skb;
+ nl_dump_check_consistent(cb, nlh);
+
memcpy(nlmsg_data(nlh), &len, sizeof(len));
if (sk_filter(sk, skb))
diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig
new file mode 100644
index 000000000000..33e095b124b3
--- /dev/null
+++ b/net/nfc/Kconfig
@@ -0,0 +1,16 @@
+#
+# NFC sybsystem configuration
+#
+
+menuconfig NFC
+ depends on NET && EXPERIMENTAL
+ tristate "NFC subsystem support (EXPERIMENTAL)"
+ default n
+ help
+ Say Y here if you want to build support for NFC (Near field
+ communication) devices.
+
+ To compile this support as a module, choose M here: the module will
+ be called nfc.
+
+source "drivers/nfc/Kconfig"
diff --git a/net/nfc/Makefile b/net/nfc/Makefile
new file mode 100644
index 000000000000..16250c353851
--- /dev/null
+++ b/net/nfc/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux NFC subsystem.
+#
+
+obj-$(CONFIG_NFC) += nfc.o
+
+nfc-objs := core.o netlink.o af_nfc.o rawsock.o
diff --git a/net/nfc/af_nfc.c b/net/nfc/af_nfc.c
new file mode 100644
index 000000000000..e982cef8f49d
--- /dev/null
+++ b/net/nfc/af_nfc.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ * Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/nfc.h>
+
+#include "nfc.h"
+
+static DEFINE_RWLOCK(proto_tab_lock);
+static const struct nfc_protocol *proto_tab[NFC_SOCKPROTO_MAX];
+
+static int nfc_sock_create(struct net *net, struct socket *sock, int proto,
+ int kern)
+{
+ int rc = -EPROTONOSUPPORT;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ if (proto < 0 || proto >= NFC_SOCKPROTO_MAX)
+ return -EINVAL;
+
+ read_lock(&proto_tab_lock);
+ if (proto_tab[proto] && try_module_get(proto_tab[proto]->owner)) {
+ rc = proto_tab[proto]->create(net, sock, proto_tab[proto]);
+ module_put(proto_tab[proto]->owner);
+ }
+ read_unlock(&proto_tab_lock);
+
+ return rc;
+}
+
+static struct net_proto_family nfc_sock_family_ops = {
+ .owner = THIS_MODULE,
+ .family = PF_NFC,
+ .create = nfc_sock_create,
+};
+
+int nfc_proto_register(const struct nfc_protocol *nfc_proto)
+{
+ int rc;
+
+ if (nfc_proto->id < 0 || nfc_proto->id >= NFC_SOCKPROTO_MAX)
+ return -EINVAL;
+
+ rc = proto_register(nfc_proto->proto, 0);
+ if (rc)
+ return rc;
+
+ write_lock(&proto_tab_lock);
+ if (proto_tab[nfc_proto->id])
+ rc = -EBUSY;
+ else
+ proto_tab[nfc_proto->id] = nfc_proto;
+ write_unlock(&proto_tab_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(nfc_proto_register);
+
+void nfc_proto_unregister(const struct nfc_protocol *nfc_proto)
+{
+ write_lock(&proto_tab_lock);
+ proto_tab[nfc_proto->id] = NULL;
+ write_unlock(&proto_tab_lock);
+
+ proto_unregister(nfc_proto->proto);
+}
+EXPORT_SYMBOL(nfc_proto_unregister);
+
+int __init af_nfc_init(void)
+{
+ return sock_register(&nfc_sock_family_ops);
+}
+
+void af_nfc_exit(void)
+{
+ sock_unregister(PF_NFC);
+}
diff --git a/net/nfc/core.c b/net/nfc/core.c
new file mode 100644
index 000000000000..b6fd4e1f2057
--- /dev/null
+++ b/net/nfc/core.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ * Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "nfc.h"
+
+#define VERSION "0.1"
+
+int nfc_devlist_generation;
+DEFINE_MUTEX(nfc_devlist_mutex);
+
+int nfc_printk(const char *level, const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int r;
+
+ va_start(args, format);
+
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ r = printk("%sNFC: %pV\n", level, &vaf);
+
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(nfc_printk);
+
+/**
+ * nfc_start_poll - start polling for nfc targets
+ *
+ * @dev: The nfc device that must start polling
+ * @protocols: bitset of nfc protocols that must be used for polling
+ *
+ * The device remains polling for targets until a target is found or
+ * the nfc_stop_poll function is called.
+ */
+int nfc_start_poll(struct nfc_dev *dev, u32 protocols)
+{
+ int rc;
+
+ nfc_dbg("dev_name=%s protocols=0x%x", dev_name(&dev->dev), protocols);
+
+ if (!protocols)
+ return -EINVAL;
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (dev->polling) {
+ rc = -EBUSY;
+ goto error;
+ }
+
+ rc = dev->ops->start_poll(dev, protocols);
+ if (!rc)
+ dev->polling = true;
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_stop_poll - stop polling for nfc targets
+ *
+ * @dev: The nfc device that must stop polling
+ */
+int nfc_stop_poll(struct nfc_dev *dev)
+{
+ int rc = 0;
+
+ nfc_dbg("dev_name=%s", dev_name(&dev->dev));
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->polling) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dev->ops->stop_poll(dev);
+ dev->polling = false;
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_activate_target - prepare the target for data exchange
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target that must be activated
+ * @protocol: nfc protocol that will be used for data exchange
+ */
+int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
+{
+ int rc;
+
+ nfc_dbg("dev_name=%s target_idx=%u protocol=%u", dev_name(&dev->dev),
+ target_idx, protocol);
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ rc = dev->ops->activate_target(dev, target_idx, protocol);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_deactivate_target - deactivate a nfc target
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target that must be deactivated
+ */
+int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx)
+{
+ int rc = 0;
+
+ nfc_dbg("dev_name=%s target_idx=%u", dev_name(&dev->dev), target_idx);
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ dev->ops->deactivate_target(dev, target_idx);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_data_exchange - transceive data
+ *
+ * @dev: The nfc device that found the target
+ * @target_idx: index of the target
+ * @skb: data to be sent
+ * @cb: callback called when the response is received
+ * @cb_context: parameter for the callback function
+ *
+ * The user must wait for the callback before calling this function again.
+ */
+int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx,
+ struct sk_buff *skb,
+ data_exchange_cb_t cb,
+ void *cb_context)
+{
+ int rc;
+
+ nfc_dbg("dev_name=%s target_idx=%u skb->len=%u", dev_name(&dev->dev),
+ target_idx, skb->len);
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ kfree_skb(skb);
+ goto error;
+ }
+
+ rc = dev->ops->data_exchange(dev, target_idx, skb, cb, cb_context);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+/**
+ * nfc_alloc_skb - allocate a skb for data exchange responses
+ *
+ * @size: size to allocate
+ * @gfp: gfp flags
+ */
+struct sk_buff *nfc_alloc_skb(unsigned int size, gfp_t gfp)
+{
+ struct sk_buff *skb;
+ unsigned int total_size;
+
+ total_size = size + 1;
+ skb = alloc_skb(total_size, gfp);
+
+ if (skb)
+ skb_reserve(skb, 1);
+
+ return skb;
+}
+EXPORT_SYMBOL(nfc_alloc_skb);
+
+/**
+ * nfc_targets_found - inform that targets were found
+ *
+ * @dev: The nfc device that found the targets
+ * @targets: array of nfc targets found
+ * @ntargets: targets array size
+ *
+ * The device driver must call this function when one or many nfc targets
+ * are found. After calling this function, the device driver must stop
+ * polling for targets.
+ */
+int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
+ int n_targets)
+{
+ int i;
+
+ nfc_dbg("dev_name=%s n_targets=%d", dev_name(&dev->dev), n_targets);
+
+ dev->polling = false;
+
+ for (i = 0; i < n_targets; i++)
+ targets[i].idx = dev->target_idx++;
+
+ spin_lock_bh(&dev->targets_lock);
+
+ dev->targets_generation++;
+
+ kfree(dev->targets);
+ dev->targets = kmemdup(targets, n_targets * sizeof(struct nfc_target),
+ GFP_ATOMIC);
+
+ if (!dev->targets) {
+ dev->n_targets = 0;
+ spin_unlock_bh(&dev->targets_lock);
+ return -ENOMEM;
+ }
+
+ dev->n_targets = n_targets;
+ spin_unlock_bh(&dev->targets_lock);
+
+ nfc_genl_targets_found(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(nfc_targets_found);
+
+static void nfc_release(struct device *d)
+{
+ struct nfc_dev *dev = to_nfc_dev(d);
+
+ nfc_dbg("dev_name=%s", dev_name(&dev->dev));
+
+ nfc_genl_data_exit(&dev->genl_data);
+ kfree(dev->targets);
+ kfree(dev);
+}
+
+struct class nfc_class = {
+ .name = "nfc",
+ .dev_release = nfc_release,
+};
+EXPORT_SYMBOL(nfc_class);
+
+static int match_idx(struct device *d, void *data)
+{
+ struct nfc_dev *dev = to_nfc_dev(d);
+ unsigned *idx = data;
+
+ return dev->idx == *idx;
+}
+
+struct nfc_dev *nfc_get_device(unsigned idx)
+{
+ struct device *d;
+
+ d = class_find_device(&nfc_class, NULL, &idx, match_idx);
+ if (!d)
+ return NULL;
+
+ return to_nfc_dev(d);
+}
+
+/**
+ * nfc_allocate_device - allocate a new nfc device
+ *
+ * @ops: device operations
+ * @supported_protocols: NFC protocols supported by the device
+ */
+struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
+ u32 supported_protocols)
+{
+ static atomic_t dev_no = ATOMIC_INIT(0);
+ struct nfc_dev *dev;
+
+ if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
+ !ops->deactivate_target || !ops->data_exchange)
+ return NULL;
+
+ if (!supported_protocols)
+ return NULL;
+
+ dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ dev->dev.class = &nfc_class;
+ dev->idx = atomic_inc_return(&dev_no) - 1;
+ dev_set_name(&dev->dev, "nfc%d", dev->idx);
+ device_initialize(&dev->dev);
+
+ dev->ops = ops;
+ dev->supported_protocols = supported_protocols;
+
+ spin_lock_init(&dev->targets_lock);
+ nfc_genl_data_init(&dev->genl_data);
+
+ /* first generation must not be 0 */
+ dev->targets_generation = 1;
+
+ return dev;
+}
+EXPORT_SYMBOL(nfc_allocate_device);
+
+/**
+ * nfc_register_device - register a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to register
+ */
+int nfc_register_device(struct nfc_dev *dev)
+{
+ int rc;
+
+ nfc_dbg("dev_name=%s", dev_name(&dev->dev));
+
+ mutex_lock(&nfc_devlist_mutex);
+ nfc_devlist_generation++;
+ rc = device_add(&dev->dev);
+ mutex_unlock(&nfc_devlist_mutex);
+
+ if (rc < 0)
+ return rc;
+
+ rc = nfc_genl_device_added(dev);
+ if (rc)
+ nfc_dbg("The userspace won't be notified that the device %s was"
+ " added", dev_name(&dev->dev));
+
+
+ return 0;
+}
+EXPORT_SYMBOL(nfc_register_device);
+
+/**
+ * nfc_unregister_device - unregister a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to unregister
+ */
+void nfc_unregister_device(struct nfc_dev *dev)
+{
+ int rc;
+
+ nfc_dbg("dev_name=%s", dev_name(&dev->dev));
+
+ mutex_lock(&nfc_devlist_mutex);
+ nfc_devlist_generation++;
+
+ /* lock to avoid unregistering a device while an operation
+ is in progress */
+ device_lock(&dev->dev);
+ device_del(&dev->dev);
+ device_unlock(&dev->dev);
+
+ mutex_unlock(&nfc_devlist_mutex);
+
+ rc = nfc_genl_device_removed(dev);
+ if (rc)
+ nfc_dbg("The userspace won't be notified that the device %s"
+ " was removed", dev_name(&dev->dev));
+
+}
+EXPORT_SYMBOL(nfc_unregister_device);
+
+static int __init nfc_init(void)
+{
+ int rc;
+
+ nfc_info("NFC Core ver %s", VERSION);
+
+ rc = class_register(&nfc_class);
+ if (rc)
+ return rc;
+
+ rc = nfc_genl_init();
+ if (rc)
+ goto err_genl;
+
+ /* the first generation must not be 0 */
+ nfc_devlist_generation = 1;
+
+ rc = rawsock_init();
+ if (rc)
+ goto err_rawsock;
+
+ rc = af_nfc_init();
+ if (rc)
+ goto err_af_nfc;
+
+ return 0;
+
+err_af_nfc:
+ rawsock_exit();
+err_rawsock:
+ nfc_genl_exit();
+err_genl:
+ class_unregister(&nfc_class);
+ return rc;
+}
+
+static void __exit nfc_exit(void)
+{
+ af_nfc_exit();
+ rawsock_exit();
+ nfc_genl_exit();
+ class_unregister(&nfc_class);
+}
+
+subsys_initcall(nfc_init);
+module_exit(nfc_exit);
+
+MODULE_AUTHOR("Lauro Ramos Venancio <lauro.venancio@openbossa.org>");
+MODULE_DESCRIPTION("NFC Core ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
new file mode 100644
index 000000000000..ccdff7953f7d
--- /dev/null
+++ b/net/nfc/netlink.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ * Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <net/genetlink.h>
+#include <linux/nfc.h>
+#include <linux/slab.h>
+
+#include "nfc.h"
+
+static struct genl_multicast_group nfc_genl_event_mcgrp = {
+ .name = NFC_GENL_MCAST_EVENT_NAME,
+};
+
+struct genl_family nfc_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = NFC_GENL_NAME,
+ .version = NFC_GENL_VERSION,
+ .maxattr = NFC_ATTR_MAX,
+};
+
+static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
+ [NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 },
+ [NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING,
+ .len = NFC_DEVICE_NAME_MAXSIZE },
+ [NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 },
+};
+
+static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
+ struct netlink_callback *cb, int flags)
+{
+ void *hdr;
+
+ nfc_dbg("entry");
+
+ hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &nfc_genl_family, flags, NFC_CMD_GET_TARGET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
+
+ NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target->idx);
+ NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS,
+ target->supported_protocols);
+ NLA_PUT_U16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res);
+ NLA_PUT_U8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res);
+
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx;
+
+ rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize,
+ nfc_genl_family.attrbuf,
+ nfc_genl_family.maxattr,
+ nfc_genl_policy);
+ if (rc < 0)
+ return ERR_PTR(rc);
+
+ if (!nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX])
+ return ERR_PTR(-EINVAL);
+
+ idx = nla_get_u32(nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ return dev;
+}
+
+static int nfc_genl_dump_targets(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int i = cb->args[0];
+ struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+ int rc;
+
+ nfc_dbg("entry");
+
+ if (!dev) {
+ dev = __get_device_from_cb(cb);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ cb->args[1] = (long) dev;
+ }
+
+ spin_lock_bh(&dev->targets_lock);
+
+ cb->seq = dev->targets_generation;
+
+ while (i < dev->n_targets) {
+ rc = nfc_genl_send_target(skb, &dev->targets[i], cb,
+ NLM_F_MULTI);
+ if (rc < 0)
+ break;
+
+ i++;
+ }
+
+ spin_unlock_bh(&dev->targets_lock);
+
+ cb->args[0] = i;
+
+ return skb->len;
+}
+
+static int nfc_genl_dump_targets_done(struct netlink_callback *cb)
+{
+ struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+
+ nfc_dbg("entry");
+
+ if (dev)
+ nfc_put_device(dev);
+
+ return 0;
+}
+
+int nfc_genl_targets_found(struct nfc_dev *dev)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ nfc_dbg("entry");
+
+ dev->genl_data.poll_req_pid = 0;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_TARGETS_FOUND);
+ if (!hdr)
+ goto free_msg;
+
+ NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+int nfc_genl_device_added(struct nfc_dev *dev)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ nfc_dbg("entry");
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_DEVICE_ADDED);
+ if (!hdr)
+ goto free_msg;
+
+ NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev));
+ NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+ NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+int nfc_genl_device_removed(struct nfc_dev *dev)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ nfc_dbg("entry");
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_DEVICE_REMOVED);
+ if (!hdr)
+ goto free_msg;
+
+ NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
+ u32 pid, u32 seq,
+ struct netlink_callback *cb,
+ int flags)
+{
+ void *hdr;
+
+ nfc_dbg("entry");
+
+ hdr = genlmsg_put(msg, pid, seq, &nfc_genl_family, flags,
+ NFC_CMD_GET_DEVICE);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (cb)
+ genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
+
+ NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev));
+ NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx);
+ NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols);
+
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nfc_genl_dump_devices(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+ struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+ bool first_call = false;
+
+ nfc_dbg("entry");
+
+ if (!iter) {
+ first_call = true;
+ iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
+ if (!iter)
+ return -ENOMEM;
+ cb->args[0] = (long) iter;
+ }
+
+ mutex_lock(&nfc_devlist_mutex);
+
+ cb->seq = nfc_devlist_generation;
+
+ if (first_call) {
+ nfc_device_iter_init(iter);
+ dev = nfc_device_iter_next(iter);
+ }
+
+ while (dev) {
+ int rc;
+
+ rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ cb, NLM_F_MULTI);
+ if (rc < 0)
+ break;
+
+ dev = nfc_device_iter_next(iter);
+ }
+
+ mutex_unlock(&nfc_devlist_mutex);
+
+ cb->args[1] = (long) dev;
+
+ return skb->len;
+}
+
+static int nfc_genl_dump_devices_done(struct netlink_callback *cb)
+{
+ struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+
+ nfc_dbg("entry");
+
+ nfc_device_iter_exit(iter);
+ kfree(iter);
+
+ return 0;
+}
+
+static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct nfc_dev *dev;
+ u32 idx;
+ int rc = -ENOBUFS;
+
+ nfc_dbg("entry");
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg) {
+ rc = -ENOMEM;
+ goto out_putdev;
+ }
+
+ rc = nfc_genl_send_device(msg, dev, info->snd_pid, info->snd_seq,
+ NULL, 0);
+ if (rc < 0)
+ goto out_free;
+
+ nfc_put_device(dev);
+
+ return genlmsg_reply(msg, info);
+
+out_free:
+ nlmsg_free(msg);
+out_putdev:
+ nfc_put_device(dev);
+ return rc;
+}
+
+static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx;
+ u32 protocols;
+
+ nfc_dbg("entry");
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+ !info->attrs[NFC_ATTR_PROTOCOLS])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+ protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->genl_data.genl_data_mutex);
+
+ rc = nfc_start_poll(dev, protocols);
+ if (!rc)
+ dev->genl_data.poll_req_pid = info->snd_pid;
+
+ mutex_unlock(&dev->genl_data.genl_data_mutex);
+
+ nfc_put_device(dev);
+ return rc;
+}
+
+static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx;
+
+ nfc_dbg("entry");
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->genl_data.genl_data_mutex);
+
+ if (dev->genl_data.poll_req_pid != info->snd_pid) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ rc = nfc_stop_poll(dev);
+ dev->genl_data.poll_req_pid = 0;
+
+out:
+ mutex_unlock(&dev->genl_data.genl_data_mutex);
+ nfc_put_device(dev);
+ return rc;
+}
+
+static struct genl_ops nfc_genl_ops[] = {
+ {
+ .cmd = NFC_CMD_GET_DEVICE,
+ .doit = nfc_genl_get_device,
+ .dumpit = nfc_genl_dump_devices,
+ .done = nfc_genl_dump_devices_done,
+ .policy = nfc_genl_policy,
+ },
+ {
+ .cmd = NFC_CMD_START_POLL,
+ .doit = nfc_genl_start_poll,
+ .policy = nfc_genl_policy,
+ },
+ {
+ .cmd = NFC_CMD_STOP_POLL,
+ .doit = nfc_genl_stop_poll,
+ .policy = nfc_genl_policy,
+ },
+ {
+ .cmd = NFC_CMD_GET_TARGET,
+ .dumpit = nfc_genl_dump_targets,
+ .done = nfc_genl_dump_targets_done,
+ .policy = nfc_genl_policy,
+ },
+};
+
+static int nfc_genl_rcv_nl_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct netlink_notify *n = ptr;
+ struct class_dev_iter iter;
+ struct nfc_dev *dev;
+
+ if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
+ goto out;
+
+ nfc_dbg("NETLINK_URELEASE event from id %d", n->pid);
+
+ nfc_device_iter_init(&iter);
+ dev = nfc_device_iter_next(&iter);
+
+ while (dev) {
+ mutex_lock(&dev->genl_data.genl_data_mutex);
+ if (dev->genl_data.poll_req_pid == n->pid) {
+ nfc_stop_poll(dev);
+ dev->genl_data.poll_req_pid = 0;
+ }
+ mutex_unlock(&dev->genl_data.genl_data_mutex);
+ dev = nfc_device_iter_next(&iter);
+ }
+
+ nfc_device_iter_exit(&iter);
+
+out:
+ return NOTIFY_DONE;
+}
+
+void nfc_genl_data_init(struct nfc_genl_data *genl_data)
+{
+ genl_data->poll_req_pid = 0;
+ mutex_init(&genl_data->genl_data_mutex);
+}
+
+void nfc_genl_data_exit(struct nfc_genl_data *genl_data)
+{
+ mutex_destroy(&genl_data->genl_data_mutex);
+}
+
+static struct notifier_block nl_notifier = {
+ .notifier_call = nfc_genl_rcv_nl_event,
+};
+
+/**
+ * nfc_genl_init() - Initialize netlink interface
+ *
+ * This initialization function registers the nfc netlink family.
+ */
+int __init nfc_genl_init(void)
+{
+ int rc;
+
+ rc = genl_register_family_with_ops(&nfc_genl_family, nfc_genl_ops,
+ ARRAY_SIZE(nfc_genl_ops));
+ if (rc)
+ return rc;
+
+ rc = genl_register_mc_group(&nfc_genl_family, &nfc_genl_event_mcgrp);
+
+ netlink_register_notifier(&nl_notifier);
+
+ return rc;
+}
+
+/**
+ * nfc_genl_exit() - Deinitialize netlink interface
+ *
+ * This exit function unregisters the nfc netlink family.
+ */
+void nfc_genl_exit(void)
+{
+ netlink_unregister_notifier(&nl_notifier);
+ genl_unregister_family(&nfc_genl_family);
+}
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
new file mode 100644
index 000000000000..aaf9832298f3
--- /dev/null
+++ b/net/nfc/nfc.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ * Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOCAL_NFC_H
+#define __LOCAL_NFC_H
+
+#include <net/nfc.h>
+#include <net/sock.h>
+
+__attribute__((format (printf, 2, 3)))
+int nfc_printk(const char *level, const char *fmt, ...);
+
+#define nfc_info(fmt, arg...) nfc_printk(KERN_INFO, fmt, ##arg)
+#define nfc_err(fmt, arg...) nfc_printk(KERN_ERR, fmt, ##arg)
+#define nfc_dbg(fmt, arg...) pr_debug(fmt "\n", ##arg)
+
+struct nfc_protocol {
+ int id;
+ struct proto *proto;
+ struct module *owner;
+ int (*create)(struct net *net, struct socket *sock,
+ const struct nfc_protocol *nfc_proto);
+};
+
+struct nfc_rawsock {
+ struct sock sk;
+ struct nfc_dev *dev;
+ u32 target_idx;
+ struct work_struct tx_work;
+ bool tx_work_scheduled;
+};
+#define nfc_rawsock(sk) ((struct nfc_rawsock *) sk)
+#define to_rawsock_sk(_tx_work) \
+ ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work))
+
+int __init rawsock_init(void);
+void rawsock_exit(void);
+
+int __init af_nfc_init(void);
+void af_nfc_exit(void);
+int nfc_proto_register(const struct nfc_protocol *nfc_proto);
+void nfc_proto_unregister(const struct nfc_protocol *nfc_proto);
+
+extern int nfc_devlist_generation;
+extern struct mutex nfc_devlist_mutex;
+
+int __init nfc_genl_init(void);
+void nfc_genl_exit(void);
+
+void nfc_genl_data_init(struct nfc_genl_data *genl_data);
+void nfc_genl_data_exit(struct nfc_genl_data *genl_data);
+
+int nfc_genl_targets_found(struct nfc_dev *dev);
+
+int nfc_genl_device_added(struct nfc_dev *dev);
+int nfc_genl_device_removed(struct nfc_dev *dev);
+
+struct nfc_dev *nfc_get_device(unsigned idx);
+
+static inline void nfc_put_device(struct nfc_dev *dev)
+{
+ put_device(&dev->dev);
+}
+
+static inline void nfc_device_iter_init(struct class_dev_iter *iter)
+{
+ class_dev_iter_init(iter, &nfc_class, NULL, NULL);
+}
+
+static inline struct nfc_dev *nfc_device_iter_next(struct class_dev_iter *iter)
+{
+ struct device *d = class_dev_iter_next(iter);
+ if (!d)
+ return NULL;
+
+ return to_nfc_dev(d);
+}
+
+static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
+{
+ class_dev_iter_exit(iter);
+}
+
+int nfc_start_poll(struct nfc_dev *dev, u32 protocols);
+
+int nfc_stop_poll(struct nfc_dev *dev);
+
+int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol);
+
+int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx);
+
+int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx,
+ struct sk_buff *skb,
+ data_exchange_cb_t cb,
+ void *cb_context);
+
+#endif /* __LOCAL_NFC_H */
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
new file mode 100644
index 000000000000..52de84a55115
--- /dev/null
+++ b/net/nfc/rawsock.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ *
+ * Authors:
+ * Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
+ * Lauro Ramos Venancio <lauro.venancio@openbossa.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <net/tcp_states.h>
+#include <linux/nfc.h>
+
+#include "nfc.h"
+
+static void rawsock_write_queue_purge(struct sock *sk)
+{
+ nfc_dbg("sk=%p", sk);
+
+ spin_lock_bh(&sk->sk_write_queue.lock);
+ __skb_queue_purge(&sk->sk_write_queue);
+ nfc_rawsock(sk)->tx_work_scheduled = false;
+ spin_unlock_bh(&sk->sk_write_queue.lock);
+}
+
+static void rawsock_report_error(struct sock *sk, int err)
+{
+ nfc_dbg("sk=%p err=%d", sk, err);
+
+ sk->sk_shutdown = SHUTDOWN_MASK;
+ sk->sk_err = -err;
+ sk->sk_error_report(sk);
+
+ rawsock_write_queue_purge(sk);
+}
+
+static int rawsock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ nfc_dbg("sock=%p", sock);
+
+ sock_orphan(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int rawsock_connect(struct socket *sock, struct sockaddr *_addr,
+ int len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_nfc *addr = (struct sockaddr_nfc *)_addr;
+ struct nfc_dev *dev;
+ int rc = 0;
+
+ nfc_dbg("sock=%p sk=%p flags=%d", sock, sk, flags);
+
+ if (!addr || len < sizeof(struct sockaddr_nfc) ||
+ addr->sa_family != AF_NFC)
+ return -EINVAL;
+
+ nfc_dbg("addr dev_idx=%u target_idx=%u protocol=%u", addr->dev_idx,
+ addr->target_idx, addr->nfc_protocol);
+
+ lock_sock(sk);
+
+ if (sock->state == SS_CONNECTED) {
+ rc = -EISCONN;
+ goto error;
+ }
+
+ dev = nfc_get_device(addr->dev_idx);
+ if (!dev) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (addr->target_idx > dev->target_idx - 1 ||
+ addr->target_idx < dev->target_idx - dev->n_targets) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (addr->target_idx > dev->target_idx - 1 ||
+ addr->target_idx < dev->target_idx - dev->n_targets) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol);
+ if (rc)
+ goto put_dev;
+
+ nfc_rawsock(sk)->dev = dev;
+ nfc_rawsock(sk)->target_idx = addr->target_idx;
+ sock->state = SS_CONNECTED;
+ sk->sk_state = TCP_ESTABLISHED;
+ sk->sk_state_change(sk);
+
+ release_sock(sk);
+ return 0;
+
+put_dev:
+ nfc_put_device(dev);
+error:
+ release_sock(sk);
+ return rc;
+}
+
+static int rawsock_add_header(struct sk_buff *skb)
+{
+
+ if (skb_cow_head(skb, 1))
+ return -ENOMEM;
+
+ *skb_push(skb, 1) = 0;
+
+ return 0;
+}
+
+static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,
+ int err)
+{
+ struct sock *sk = (struct sock *) context;
+
+ BUG_ON(in_irq());
+
+ nfc_dbg("sk=%p err=%d", sk, err);
+
+ if (err)
+ goto error;
+
+ err = rawsock_add_header(skb);
+ if (err)
+ goto error;
+
+ err = sock_queue_rcv_skb(sk, skb);
+ if (err)
+ goto error;
+
+ spin_lock_bh(&sk->sk_write_queue.lock);
+ if (!skb_queue_empty(&sk->sk_write_queue))
+ schedule_work(&nfc_rawsock(sk)->tx_work);
+ else
+ nfc_rawsock(sk)->tx_work_scheduled = false;
+ spin_unlock_bh(&sk->sk_write_queue.lock);
+
+ sock_put(sk);
+ return;
+
+error:
+ rawsock_report_error(sk, err);
+ sock_put(sk);
+}
+
+static void rawsock_tx_work(struct work_struct *work)
+{
+ struct sock *sk = to_rawsock_sk(work);
+ struct nfc_dev *dev = nfc_rawsock(sk)->dev;
+ u32 target_idx = nfc_rawsock(sk)->target_idx;
+ struct sk_buff *skb;
+ int rc;
+
+ nfc_dbg("sk=%p target_idx=%u", sk, target_idx);
+
+ if (sk->sk_shutdown & SEND_SHUTDOWN) {
+ rawsock_write_queue_purge(sk);
+ return;
+ }
+
+ skb = skb_dequeue(&sk->sk_write_queue);
+
+ sock_hold(sk);
+ rc = nfc_data_exchange(dev, target_idx, skb,
+ rawsock_data_exchange_complete, sk);
+ if (rc) {
+ rawsock_report_error(sk, rc);
+ sock_put(sk);
+ }
+}
+
+static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int rc;
+
+ nfc_dbg("sock=%p sk=%p len=%zu", sock, sk, len);
+
+ if (msg->msg_namelen)
+ return -EOPNOTSUPP;
+
+ if (sock->state != SS_CONNECTED)
+ return -ENOTCONN;
+
+ skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT,
+ &rc);
+ if (!skb)
+ return rc;
+
+ rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ if (rc < 0) {
+ kfree_skb(skb);
+ return rc;
+ }
+
+ spin_lock_bh(&sk->sk_write_queue.lock);
+ __skb_queue_tail(&sk->sk_write_queue, skb);
+ if (!nfc_rawsock(sk)->tx_work_scheduled) {
+ schedule_work(&nfc_rawsock(sk)->tx_work);
+ nfc_rawsock(sk)->tx_work_scheduled = true;
+ }
+ spin_unlock_bh(&sk->sk_write_queue.lock);
+
+ return len;
+}
+
+static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ int noblock = flags & MSG_DONTWAIT;
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int copied;
+ int rc;
+
+ nfc_dbg("sock=%p sk=%p len=%zu flags=%d", sock, sk, len, flags);
+
+ skb = skb_recv_datagram(sk, flags, noblock, &rc);
+ if (!skb)
+ return rc;
+
+ msg->msg_namelen = 0;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+ skb_free_datagram(sk, skb);
+
+ return rc ? : copied;
+}
+
+
+static const struct proto_ops rawsock_ops = {
+ .family = PF_NFC,
+ .owner = THIS_MODULE,
+ .release = rawsock_release,
+ .bind = sock_no_bind,
+ .connect = rawsock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = rawsock_sendmsg,
+ .recvmsg = rawsock_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static void rawsock_destruct(struct sock *sk)
+{
+ nfc_dbg("sk=%p", sk);
+
+ if (sk->sk_state == TCP_ESTABLISHED) {
+ nfc_deactivate_target(nfc_rawsock(sk)->dev,
+ nfc_rawsock(sk)->target_idx);
+ nfc_put_device(nfc_rawsock(sk)->dev);
+ }
+
+ skb_queue_purge(&sk->sk_receive_queue);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ nfc_err("Freeing alive NFC raw socket %p", sk);
+ return;
+ }
+}
+
+static int rawsock_create(struct net *net, struct socket *sock,
+ const struct nfc_protocol *nfc_proto)
+{
+ struct sock *sk;
+
+ nfc_dbg("sock=%p", sock);
+
+ if (sock->type != SOCK_SEQPACKET)
+ return -ESOCKTNOSUPPORT;
+
+ sock->ops = &rawsock_ops;
+
+ sk = sk_alloc(net, PF_NFC, GFP_KERNEL, nfc_proto->proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sk->sk_protocol = nfc_proto->id;
+ sk->sk_destruct = rawsock_destruct;
+ sock->state = SS_UNCONNECTED;
+
+ INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work);
+ nfc_rawsock(sk)->tx_work_scheduled = false;
+
+ return 0;
+}
+
+static struct proto rawsock_proto = {
+ .name = "NFC_RAW",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct nfc_rawsock),
+};
+
+static const struct nfc_protocol rawsock_nfc_proto = {
+ .id = NFC_SOCKPROTO_RAW,
+ .proto = &rawsock_proto,
+ .owner = THIS_MODULE,
+ .create = rawsock_create
+};
+
+int __init rawsock_init(void)
+{
+ int rc;
+
+ rc = nfc_proto_register(&rawsock_nfc_proto);
+
+ return rc;
+}
+
+void rawsock_exit(void)
+{
+ nfc_proto_unregister(&rawsock_nfc_proto);
+}
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 493b939970cd..832f6574e4ed 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -170,7 +170,9 @@ void __cfg80211_send_deauth(struct net_device *dev,
break;
}
if (wdev->authtry_bsses[i] &&
- memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
+ memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid,
+ ETH_ALEN) == 0 &&
+ memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) == 0) {
cfg80211_unhold_bss(wdev->authtry_bsses[i]);
cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
wdev->authtry_bsses[i] = NULL;
@@ -1082,3 +1084,14 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);
}
EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
+
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index cea338150d05..6a82c898f831 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -176,6 +176,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
[NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
+ [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
};
/* policy for the key attributes */
@@ -206,6 +207,14 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
[NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
};
+/* policy for GTK rekey offload attributes */
+static const struct nla_policy
+nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
+ [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
+ [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
+ [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
+};
+
/* ifidx get helper */
static int nl80211_get_ifidx(struct netlink_callback *cb)
{
@@ -3632,7 +3641,8 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,
return err;
}
-static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
+ u32 seq, int flags,
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_internal_bss *intbss)
@@ -3644,11 +3654,13 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
ASSERT_WDEV_LOCK(wdev);
- hdr = nl80211hdr_put(msg, pid, seq, flags,
+ hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
NL80211_CMD_NEW_SCAN_RESULTS);
if (!hdr)
return -1;
+ genl_dump_check_consistent(cb, hdr, &nl80211_fam);
+
NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
@@ -3737,11 +3749,12 @@ static int nl80211_dump_scan(struct sk_buff *skb,
spin_lock_bh(&rdev->bss_lock);
cfg80211_bss_expire(rdev);
+ cb->seq = rdev->bss_generation;
+
list_for_each_entry(scan, &rdev->bss_list, list) {
if (++idx <= start)
continue;
- if (nl80211_send_bss(skb,
- NETLINK_CB(cb->skb).pid,
+ if (nl80211_send_bss(skb, cb,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
rdev, wdev, scan) < 0) {
idx--;
@@ -3765,10 +3778,6 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
void *hdr;
struct nlattr *infoattr;
- /* Survey without a channel doesn't make sense */
- if (!survey->channel)
- return -EINVAL;
-
hdr = nl80211hdr_put(msg, pid, seq, flags,
NL80211_CMD_NEW_SURVEY_RESULTS);
if (!hdr)
@@ -3831,6 +3840,8 @@ static int nl80211_dump_survey(struct sk_buff *skb,
}
while (1) {
+ struct ieee80211_channel *chan;
+
res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
&survey);
if (res == -ENOENT)
@@ -3838,6 +3849,19 @@ static int nl80211_dump_survey(struct sk_buff *skb,
if (res)
goto out_err;
+ /* Survey without a channel doesn't make sense */
+ if (!survey.channel) {
+ res = -EINVAL;
+ goto out;
+ }
+
+ chan = ieee80211_get_channel(&dev->wiphy,
+ survey.channel->center_freq);
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+ survey_idx++;
+ continue;
+ }
+
if (nl80211_send_survey(skb,
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -4372,6 +4396,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_testmode_dump(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct cfg80211_registered_device *dev;
+ int err;
+ long phy_idx;
+ void *data = NULL;
+ int data_len = 0;
+
+ if (cb->args[0]) {
+ /*
+ * 0 is a valid index, but not valid for args[0],
+ * so we need to offset by 1.
+ */
+ phy_idx = cb->args[0] - 1;
+ } else {
+ err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+ nl80211_fam.attrbuf, nl80211_fam.maxattr,
+ nl80211_policy);
+ if (err)
+ return err;
+ if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY])
+ return -EINVAL;
+ phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
+ if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
+ cb->args[1] =
+ (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
+ }
+
+ if (cb->args[1]) {
+ data = nla_data((void *)cb->args[1]);
+ data_len = nla_len((void *)cb->args[1]);
+ }
+
+ mutex_lock(&cfg80211_mutex);
+ dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
+ if (!dev) {
+ mutex_unlock(&cfg80211_mutex);
+ return -ENOENT;
+ }
+ cfg80211_lock_rdev(dev);
+ mutex_unlock(&cfg80211_mutex);
+
+ if (!dev->ops->testmode_dump) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ NL80211_CMD_TESTMODE);
+ struct nlattr *tmdata;
+
+ if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+
+ tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
+ if (!tmdata) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+ err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
+ data, data_len);
+ nla_nest_end(skb, tmdata);
+
+ if (err == -ENOBUFS || err == -ENOENT) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ } else if (err) {
+ genlmsg_cancel(skb, hdr);
+ goto out_err;
+ }
+
+ genlmsg_end(skb, hdr);
+ }
+
+ err = skb->len;
+ /* see above */
+ cb->args[0] = phy_idx + 1;
+ out_err:
+ cfg80211_unlock_rdev(dev);
+ return err;
+}
+
static struct sk_buff *
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
int approxlen, u32 pid, u32 seq, gfp_t gfp)
@@ -5318,6 +5429,57 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct nlattr *tb[NUM_NL80211_REKEY_DATA];
+ struct cfg80211_gtk_rekey_data rekey_data;
+ int err;
+
+ if (!info->attrs[NL80211_ATTR_REKEY_DATA])
+ return -EINVAL;
+
+ err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
+ nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
+ nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
+ nl80211_rekey_policy);
+ if (err)
+ return err;
+
+ if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
+ return -ERANGE;
+ if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
+ return -ERANGE;
+ if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
+ return -ERANGE;
+
+ memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
+ NL80211_KEK_LEN);
+ memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
+ NL80211_KCK_LEN);
+ memcpy(rekey_data.replay_ctr,
+ nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
+ NL80211_REPLAY_CTR_LEN);
+
+ wdev_lock(wdev);
+ if (!wdev->current_bss) {
+ err = -ENOTCONN;
+ goto out;
+ }
+
+ if (!rdev->ops->set_rekey_data) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -5669,6 +5831,7 @@ static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_TESTMODE,
.doit = nl80211_testmode_do,
+ .dumpit = nl80211_testmode_dump,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WIPHY |
@@ -5848,6 +6011,14 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
+ .doit = nl80211_set_rekey_data,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -6792,6 +6963,51 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ struct nlattr *rekey_attr;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+
+ rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
+ if (!rekey_attr)
+ goto nla_put_failure;
+
+ NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
+ NL80211_REPLAY_CTR_LEN, replay_ctr);
+
+ nla_nest_end(msg, rekey_attr);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 2f1bfb87a651..5d69c56400ae 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -109,4 +109,8 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
u32 num_packets, gfp_t gfp);
+void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp);
+
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index ae0c2256ba3b..1c4672e35144 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -132,18 +132,17 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
bool driver_initiated)
{
- int err;
struct net_device *dev;
lockdep_assert_held(&rdev->sched_scan_mtx);
if (!rdev->sched_scan_req)
- return 0;
+ return -ENOENT;
dev = rdev->sched_scan_req->dev;
if (!driver_initiated) {
- err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
+ int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
if (err)
return err;
}
@@ -153,7 +152,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
kfree(rdev->sched_scan_req);
rdev->sched_scan_req = NULL;
- return err;
+ return 0;
}
static void bss_release(struct kref *ref)