summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2012-09-30 02:30:16 -0400
committerDavid S. Miller <davem@davemloft.net>2012-09-30 02:30:16 -0400
commita248afdc1b5916c2bfd007233112333d85aa28f6 (patch)
tree49d4a0b8fdcf7624ea6b0bdf0af2567d7392210e /net
parentd379142bc4d9b78cdd9fc5aa696ca1ea083fb7d4 (diff)
parentc487606f835a93a725bac1aefd536be98f22474d (diff)
downloadlinux-stable-a248afdc1b5916c2bfd007233112333d85aa28f6.tar.gz
linux-stable-a248afdc1b5916c2bfd007233112333d85aa28f6.tar.bz2
linux-stable-a248afdc1b5916c2bfd007233112333d85aa28f6.zip
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says: ==================== Here is another batch of updates intended for 3.7... Highlights include an hci_connect re-write in Bluetooth, HCI/LLC layer separation in NFC, removal of the raw pn544 NFC driver, NFC LLCP raw sockets support, improved IBSS auth frame handling in mac80211, full-MAC AP mode notification support in mac80211, a lot of attention paid to brcmfmac, and the usual level of updates to iwlwifi, ath9k, mwifiex, and rt2x00, and various other updates. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/af_bluetooth.c10
-rw-r--r--net/bluetooth/hci_conn.c100
-rw-r--r--net/bluetooth/hci_core.c6
-rw-r--r--net/bluetooth/hci_event.c99
-rw-r--r--net/bluetooth/l2cap_core.c17
-rw-r--r--net/bluetooth/mgmt.c28
-rw-r--r--net/mac80211/agg-tx.c2
-rw-r--r--net/mac80211/cfg.c37
-rw-r--r--net/mac80211/chan.c67
-rw-r--r--net/mac80211/debugfs.c4
-rw-r--r--net/mac80211/ibss.c35
-rw-r--r--net/mac80211/ieee80211_i.h23
-rw-r--r--net/mac80211/iface.c15
-rw-r--r--net/mac80211/key.c2
-rw-r--r--net/mac80211/main.c10
-rw-r--r--net/mac80211/mesh_plink.c3
-rw-r--r--net/mac80211/mlme.c207
-rw-r--r--net/mac80211/offchannel.c3
-rw-r--r--net/mac80211/scan.c41
-rw-r--r--net/mac80211/sta_info.c123
-rw-r--r--net/mac80211/sta_info.h2
-rw-r--r--net/mac80211/status.c48
-rw-r--r--net/mac80211/tx.c2
-rw-r--r--net/mac80211/util.c51
-rw-r--r--net/nfc/core.c13
-rw-r--r--net/nfc/hci/Makefile4
-rw-r--r--net/nfc/hci/command.c45
-rw-r--r--net/nfc/hci/core.c336
-rw-r--r--net/nfc/hci/hci.h15
-rw-r--r--net/nfc/hci/hcp.c6
-rw-r--r--net/nfc/hci/llc.c170
-rw-r--r--net/nfc/hci/llc.h69
-rw-r--r--net/nfc/hci/llc_nop.c99
-rw-r--r--net/nfc/hci/llc_shdlc.c (renamed from net/nfc/hci/shdlc.c)544
-rw-r--r--net/nfc/llcp/commands.c2
-rw-r--r--net/nfc/llcp/llcp.c131
-rw-r--r--net/nfc/llcp/llcp.h6
-rw-r--r--net/nfc/llcp/sock.c93
-rw-r--r--net/nfc/nci/core.c91
-rw-r--r--net/nfc/nci/ntf.c52
-rw-r--r--net/nfc/nci/rsp.c14
-rw-r--r--net/nfc/netlink.c46
-rw-r--r--net/rfkill/core.c8
-rw-r--r--net/wireless/mlme.c11
-rw-r--r--net/wireless/nl80211.c34
-rw-r--r--net/wireless/nl80211.h5
-rw-r--r--net/wireless/reg.c39
47 files changed, 1849 insertions, 919 deletions
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 58f9762b339a..9d49ee6d7219 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -567,8 +567,6 @@ static void bt_seq_stop(struct seq_file *seq, void *v)
static int bt_seq_show(struct seq_file *seq, void *v)
{
- struct sock *sk;
- struct bt_sock *bt;
struct bt_seq_state *s = seq->private;
struct bt_sock_list *l = s->l;
bdaddr_t src_baswapped, dst_baswapped;
@@ -583,8 +581,8 @@ static int bt_seq_show(struct seq_file *seq, void *v)
seq_putc(seq, '\n');
} else {
- sk = sk_entry(v);
- bt = bt_sk(sk);
+ struct sock *sk = sk_entry(v);
+ struct bt_sock *bt = bt_sk(sk);
baswap(&src_baswapped, &bt->src);
baswap(&dst_baswapped, &bt->dst);
@@ -624,7 +622,7 @@ static int bt_seq_open(struct inode *inode, struct file *file)
sk_list = PDE(inode)->data;
s = __seq_open_private(file, &bt_seq_ops,
sizeof(struct bt_seq_state));
- if (s == NULL)
+ if (!s)
return -ENOMEM;
s->l = sk_list;
@@ -646,7 +644,7 @@ int bt_procfs_init(struct module* module, struct net *net, const char *name,
sk_list->fops.release = seq_release_private;
pde = proc_net_fops_create(net, name, 0, &sk_list->fops);
- if (pde == NULL)
+ if (!pde)
return -ENOMEM;
pde->data = sk_list;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 3c094e78dde9..b9196a44f759 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -31,7 +31,7 @@
#include <net/bluetooth/a2mp.h>
#include <net/bluetooth/smp.h>
-static void hci_le_connect(struct hci_conn *conn)
+static void hci_le_create_connection(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_create_conn cp;
@@ -55,12 +55,12 @@ static void hci_le_connect(struct hci_conn *conn)
hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
}
-static void hci_le_connect_cancel(struct hci_conn *conn)
+static void hci_le_create_connection_cancel(struct hci_conn *conn)
{
hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
}
-void hci_acl_connect(struct hci_conn *conn)
+static void hci_acl_create_connection(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct inquiry_entry *ie;
@@ -104,7 +104,7 @@ void hci_acl_connect(struct hci_conn *conn)
hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp);
}
-static void hci_acl_connect_cancel(struct hci_conn *conn)
+static void hci_acl_create_connection_cancel(struct hci_conn *conn)
{
struct hci_cp_create_conn_cancel cp;
@@ -130,7 +130,7 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
}
-void hci_add_sco(struct hci_conn *conn, __u16 handle)
+static void hci_add_sco(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_add_sco cp;
@@ -246,9 +246,9 @@ static void hci_conn_timeout(struct work_struct *work)
case BT_CONNECT2:
if (conn->out) {
if (conn->type == ACL_LINK)
- hci_acl_connect_cancel(conn);
+ hci_acl_create_connection_cancel(conn);
else if (conn->type == LE_LINK)
- hci_le_connect_cancel(conn);
+ hci_le_create_connection_cancel(conn);
}
break;
case BT_CONFIG:
@@ -471,40 +471,37 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
}
EXPORT_SYMBOL(hci_get_route);
-/* Create SCO, ACL or LE connection.
- * Device _must_ be locked */
-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
- __u8 dst_type, __u8 sec_level, __u8 auth_type)
+static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
+ u8 dst_type, u8 sec_level, u8 auth_type)
{
- struct hci_conn *acl;
- struct hci_conn *sco;
struct hci_conn *le;
- BT_DBG("%s dst %s", hdev->name, batostr(dst));
+ le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+ if (!le) {
+ le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+ if (le)
+ return ERR_PTR(-EBUSY);
- if (type == LE_LINK) {
- le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
- if (!le) {
- le = hci_conn_hash_lookup_state(hdev, LE_LINK,
- BT_CONNECT);
- if (le)
- return ERR_PTR(-EBUSY);
+ le = hci_conn_add(hdev, LE_LINK, dst);
+ if (!le)
+ return ERR_PTR(-ENOMEM);
- le = hci_conn_add(hdev, LE_LINK, dst);
- if (!le)
- return ERR_PTR(-ENOMEM);
+ le->dst_type = bdaddr_to_le(dst_type);
+ hci_le_create_connection(le);
+ }
- le->dst_type = bdaddr_to_le(dst_type);
- hci_le_connect(le);
- }
+ le->pending_sec_level = sec_level;
+ le->auth_type = auth_type;
- le->pending_sec_level = sec_level;
- le->auth_type = auth_type;
+ hci_conn_hold(le);
- hci_conn_hold(le);
+ return le;
+}
- return le;
- }
+static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
+ u8 sec_level, u8 auth_type)
+{
+ struct hci_conn *acl;
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (!acl) {
@@ -519,10 +516,20 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
acl->sec_level = BT_SECURITY_LOW;
acl->pending_sec_level = sec_level;
acl->auth_type = auth_type;
- hci_acl_connect(acl);
+ hci_acl_create_connection(acl);
}
- if (type == ACL_LINK)
+ return acl;
+}
+
+static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
+ bdaddr_t *dst, u8 sec_level, u8 auth_type)
+{
+ struct hci_conn *acl;
+ struct hci_conn *sco;
+
+ acl = hci_connect_acl(hdev, dst, sec_level, auth_type);
+ if (IS_ERR(acl))
return acl;
sco = hci_conn_hash_lookup_ba(hdev, type, dst);
@@ -556,6 +563,25 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
return sco;
}
+/* Create SCO, ACL or LE connection. */
+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
+ __u8 dst_type, __u8 sec_level, __u8 auth_type)
+{
+ BT_DBG("%s dst %s type 0x%x", hdev->name, batostr(dst), type);
+
+ switch (type) {
+ case LE_LINK:
+ return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type);
+ case ACL_LINK:
+ return hci_connect_acl(hdev, dst, sec_level, auth_type);
+ case SCO_LINK:
+ case ESCO_LINK:
+ return hci_connect_sco(hdev, type, dst, sec_level, auth_type);
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
/* Check link security requirement */
int hci_conn_check_link_mode(struct hci_conn *conn)
{
@@ -775,7 +801,7 @@ void hci_conn_check_pending(struct hci_dev *hdev)
conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2);
if (conn)
- hci_acl_connect(conn);
+ hci_acl_create_connection(conn);
hci_dev_unlock(hdev);
}
@@ -913,7 +939,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
return chan;
}
-int hci_chan_del(struct hci_chan *chan)
+void hci_chan_del(struct hci_chan *chan)
{
struct hci_conn *conn = chan->conn;
struct hci_dev *hdev = conn->hdev;
@@ -926,8 +952,6 @@ int hci_chan_del(struct hci_chan *chan)
skb_queue_purge(&chan->data_q);
kfree(chan);
-
- return 0;
}
void hci_chan_list_flush(struct hci_conn *conn)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 8806869ad4d6..8a0ce706aebd 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -231,6 +231,9 @@ static void amp_init(struct hci_dev *hdev)
/* Read Local AMP Info */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
+
+ /* Read Data Blk size */
+ hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
}
static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
@@ -268,7 +271,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
BT_ERR("Unknown device type %d", hdev->dev_type);
break;
}
-
}
static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
@@ -1654,6 +1656,7 @@ struct hci_dev *hci_alloc_dev(void)
INIT_LIST_HEAD(&hdev->link_keys);
INIT_LIST_HEAD(&hdev->long_term_keys);
INIT_LIST_HEAD(&hdev->remote_oob_data);
+ INIT_LIST_HEAD(&hdev->conn_hash.list);
INIT_WORK(&hdev->rx_work, hci_rx_work);
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
@@ -1676,7 +1679,6 @@ struct hci_dev *hci_alloc_dev(void)
hci_init_sysfs(hdev);
discovery_init(hdev);
- hci_conn_hash_init(hdev);
return hdev;
}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 4fd2cf3bcd05..2022b43c7353 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -29,6 +29,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/mgmt.h>
/* Handle HCI Event packets */
@@ -303,7 +304,7 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (status != 0) {
+ if (status) {
mgmt_write_scan_failed(hdev, param, status);
hdev->discov_timeout = 0;
goto done;
@@ -925,7 +926,7 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
- if (rp->status != 0)
+ if (rp->status)
goto unlock;
cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
@@ -1891,6 +1892,22 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
}
+static u8 hci_to_mgmt_reason(u8 err)
+{
+ switch (err) {
+ case HCI_ERROR_CONNECTION_TIMEOUT:
+ return MGMT_DEV_DISCONN_TIMEOUT;
+ case HCI_ERROR_REMOTE_USER_TERM:
+ case HCI_ERROR_REMOTE_LOW_RESOURCES:
+ case HCI_ERROR_REMOTE_POWER_OFF:
+ return MGMT_DEV_DISCONN_REMOTE;
+ case HCI_ERROR_LOCAL_HOST_TERM:
+ return MGMT_DEV_DISCONN_LOCAL_HOST;
+ default:
+ return MGMT_DEV_DISCONN_UNKNOWN;
+ }
+}
+
static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_disconn_complete *ev = (void *) skb->data;
@@ -1909,12 +1926,15 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) &&
(conn->type == ACL_LINK || conn->type == LE_LINK)) {
- if (ev->status != 0)
+ if (ev->status) {
mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
conn->dst_type, ev->status);
- else
+ } else {
+ u8 reason = hci_to_mgmt_reason(ev->reason);
+
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
- conn->dst_type);
+ conn->dst_type, reason);
+ }
}
if (ev->status == 0) {
@@ -3259,6 +3279,65 @@ static void hci_user_passkey_request_evt(struct hci_dev *hdev,
mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0);
}
+static void hci_user_passkey_notify_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_user_passkey_notify *ev = (void *) skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s", hdev->name);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+ if (!conn)
+ return;
+
+ conn->passkey_notify = __le32_to_cpu(ev->passkey);
+ conn->passkey_entered = 0;
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_user_passkey_notify(hdev, &conn->dst, conn->type,
+ conn->dst_type, conn->passkey_notify,
+ conn->passkey_entered);
+}
+
+static void hci_keypress_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_keypress_notify *ev = (void *) skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s", hdev->name);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+ if (!conn)
+ return;
+
+ switch (ev->type) {
+ case HCI_KEYPRESS_STARTED:
+ conn->passkey_entered = 0;
+ return;
+
+ case HCI_KEYPRESS_ENTERED:
+ conn->passkey_entered++;
+ break;
+
+ case HCI_KEYPRESS_ERASED:
+ conn->passkey_entered--;
+ break;
+
+ case HCI_KEYPRESS_CLEARED:
+ conn->passkey_entered = 0;
+ break;
+
+ case HCI_KEYPRESS_COMPLETED:
+ return;
+ }
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_user_passkey_notify(hdev, &conn->dst, conn->type,
+ conn->dst_type, conn->passkey_notify,
+ conn->passkey_entered);
+}
+
static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -3278,7 +3357,7 @@ static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
* initiated the authentication. A traditional auth_complete
* event gets always produced as initiator and is also mapped to
* the mgmt_auth_failed event */
- if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0)
+ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status)
mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
ev->status);
@@ -3623,6 +3702,14 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_user_passkey_request_evt(hdev, skb);
break;
+ case HCI_EV_USER_PASSKEY_NOTIFY:
+ hci_user_passkey_notify_evt(hdev, skb);
+ break;
+
+ case HCI_EV_KEYPRESS_NOTIFY:
+ hci_keypress_notify_evt(hdev, skb);
+ break;
+
case HCI_EV_SIMPLE_PAIR_COMPLETE:
hci_simple_pair_complete_evt(hdev, skb);
break;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index bda526edfb9e..a91239dcda41 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -406,7 +406,7 @@ struct l2cap_chan *l2cap_chan_create(void)
chan->state = BT_OPEN;
- atomic_set(&chan->refcnt, 1);
+ kref_init(&chan->kref);
/* This flag is cleared in l2cap_chan_ready() */
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
@@ -416,8 +416,10 @@ struct l2cap_chan *l2cap_chan_create(void)
return chan;
}
-static void l2cap_chan_destroy(struct l2cap_chan *chan)
+static void l2cap_chan_destroy(struct kref *kref)
{
+ struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref);
+
BT_DBG("chan %p", chan);
write_lock(&chan_list_lock);
@@ -429,17 +431,16 @@ static void l2cap_chan_destroy(struct l2cap_chan *chan)
void l2cap_chan_hold(struct l2cap_chan *c)
{
- BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt));
+ BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
- atomic_inc(&c->refcnt);
+ kref_get(&c->kref);
}
void l2cap_chan_put(struct l2cap_chan *c)
{
- BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt));
+ BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
- if (atomic_dec_and_test(&c->refcnt))
- l2cap_chan_destroy(c);
+ kref_put(&c->kref, l2cap_chan_destroy);
}
void l2cap_chan_set_defaults(struct l2cap_chan *chan)
@@ -1448,7 +1449,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
int err;
BT_DBG("%s -> %s (type %u) psm 0x%2.2x", batostr(src), batostr(dst),
- dst_type, __le16_to_cpu(chan->psm));
+ dst_type, __le16_to_cpu(psm));
hdev = hci_get_route(dst, src);
if (!hdev)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index e3296319606b..aa2ea0a8142c 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -35,7 +35,7 @@
bool enable_hs;
#define MGMT_VERSION 1
-#define MGMT_REVISION 1
+#define MGMT_REVISION 2
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -99,6 +99,7 @@ static const u16 mgmt_events[] = {
MGMT_EV_DEVICE_BLOCKED,
MGMT_EV_DEVICE_UNBLOCKED,
MGMT_EV_DEVICE_UNPAIRED,
+ MGMT_EV_PASSKEY_NOTIFY,
};
/*
@@ -3093,16 +3094,17 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
}
int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 link_type, u8 addr_type)
+ u8 link_type, u8 addr_type, u8 reason)
{
- struct mgmt_addr_info ev;
+ struct mgmt_ev_device_disconnected ev;
struct sock *sk = NULL;
int err;
mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
- bacpy(&ev.bdaddr, bdaddr);
- ev.type = link_to_bdaddr(link_type, addr_type);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = link_to_bdaddr(link_type, addr_type);
+ ev.reason = reason;
err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
sk);
@@ -3291,6 +3293,22 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
MGMT_OP_USER_PASSKEY_NEG_REPLY);
}
+int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 link_type, u8 addr_type, u32 passkey,
+ u8 entered)
+{
+ struct mgmt_ev_passkey_notify ev;
+
+ BT_DBG("%s", hdev->name);
+
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = link_to_bdaddr(link_type, addr_type);
+ ev.passkey = __cpu_to_le32(passkey);
+ ev.entered = entered;
+
+ return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
+}
+
int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 status)
{
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index d0deb3edae21..3195a6307f50 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -869,7 +869,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
} else {
___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR,
- true);
+ false);
}
out:
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 03fe6d1cff42..05f3a313db88 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -20,7 +20,8 @@
#include "rate.h"
#include "mesh.h"
-static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, char *name,
+static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
+ const char *name,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
@@ -170,6 +171,38 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
}
}
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
+ key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ /* Keys without a station are used for TX only */
+ if (key->sta && test_sta_flag(key->sta, WLAN_STA_MFP))
+ key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ /* no MFP (yet) */
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+#ifdef CONFIG_MAC80211_MESH
+ if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
+ key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+ break;
+#endif
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ /* shouldn't happen */
+ WARN_ON_ONCE(1);
+ break;
+ }
+
err = ieee80211_key_link(key, sdata, sta);
if (err)
ieee80211_key_free(sdata->local, key);
@@ -2038,9 +2071,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
*/
if (!sdata->u.mgd.associated ||
sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
- mutex_lock(&sdata->local->iflist_mtx);
ieee80211_recalc_smps(sdata->local);
- mutex_unlock(&sdata->local->iflist_mtx);
return 0;
}
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index f0f87e5a1d35..0bfc914ddd15 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -68,16 +68,14 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,
return mode;
}
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- enum nl80211_channel_type chantype)
+static enum nl80211_channel_type
+ieee80211_get_superchan(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *tmp;
enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
- bool result;
+ struct ieee80211_sub_if_data *tmp;
mutex_lock(&local->iflist_mtx);
-
list_for_each_entry(tmp, &local->interfaces, list) {
if (tmp == sdata)
continue;
@@ -103,39 +101,70 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
break;
}
}
+ mutex_unlock(&local->iflist_mtx);
- switch (superchan) {
+ return superchan;
+}
+
+static bool
+ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
+ enum nl80211_channel_type chantype2,
+ enum nl80211_channel_type *compat)
+{
+ /*
+ * start out with chantype1 being the result,
+ * overwriting later if needed
+ */
+ if (compat)
+ *compat = chantype1;
+
+ switch (chantype1) {
case NL80211_CHAN_NO_HT:
+ if (compat)
+ *compat = chantype2;
+ break;
case NL80211_CHAN_HT20:
/*
* allow any change that doesn't go to no-HT
* (if it already is no-HT no change is needed)
*/
- if (chantype == NL80211_CHAN_NO_HT)
+ if (chantype2 == NL80211_CHAN_NO_HT)
break;
- superchan = chantype;
+ if (compat)
+ *compat = chantype2;
break;
case NL80211_CHAN_HT40PLUS:
case NL80211_CHAN_HT40MINUS:
/* allow smaller bandwidth and same */
- if (chantype == NL80211_CHAN_NO_HT)
+ if (chantype2 == NL80211_CHAN_NO_HT)
break;
- if (chantype == NL80211_CHAN_HT20)
+ if (chantype2 == NL80211_CHAN_HT20)
break;
- if (superchan == chantype)
+ if (chantype2 == chantype1)
break;
- result = false;
- goto out;
+ return false;
}
- local->_oper_channel_type = superchan;
+ return true;
+}
+
+bool ieee80211_set_channel_type(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ enum nl80211_channel_type chantype)
+{
+ enum nl80211_channel_type superchan;
+ enum nl80211_channel_type compatchan;
+
+ superchan = ieee80211_get_superchan(local, sdata);
+ if (!ieee80211_channel_types_are_compatible(superchan, chantype,
+ &compatchan))
+ return false;
+
+ local->_oper_channel_type = compatchan;
if (sdata)
sdata->vif.bss_conf.channel_type = chantype;
- result = true;
- out:
- mutex_unlock(&local->iflist_mtx);
+ return true;
- return result;
}
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 97173f8144d4..466f4b45dd94 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -70,6 +70,7 @@ DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
+#ifdef CONFIG_PM
static ssize_t reset_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -88,6 +89,7 @@ static const struct file_operations reset_ops = {
.open = simple_open,
.llseek = noop_llseek,
};
+#endif
static ssize_t hwflags_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -245,7 +247,9 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(total_ps_buffered);
DEBUGFS_ADD(wep_iv);
DEBUGFS_ADD(queues);
+#ifdef CONFIG_PM
DEBUGFS_ADD_MODE(reset, 0200);
+#endif
DEBUGFS_ADD(hwflags);
DEBUGFS_ADD(user_power);
DEBUGFS_ADD(power);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index a9d93285dba7..5f3620f0bc0a 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -278,7 +278,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
if (auth && !sdata->u.ibss.auth_frame_registrations) {
ibss_dbg(sdata,
"TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
- sdata->vif.addr, sdata->u.ibss.bssid, addr);
+ sdata->vif.addr, addr, sdata->u.ibss.bssid);
ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0,
addr, sdata->u.ibss.bssid, NULL, 0, 0);
}
@@ -332,11 +332,27 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
return ieee80211_ibss_finish_sta(sta, auth);
}
+static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ u16 reason = le16_to_cpu(mgmt->u.deauth.reason_code);
+
+ if (len < IEEE80211_DEAUTH_FRAME_LEN)
+ return;
+
+ ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM BSSID=%pM (reason: %d)\n",
+ mgmt->sa, mgmt->da, mgmt->bssid, reason);
+ sta_info_destroy_addr(sdata, mgmt->sa);
+}
+
static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len)
{
u16 auth_alg, auth_transaction;
+ struct sta_info *sta;
+ u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
lockdep_assert_held(&sdata->u.ibss.mtx);
@@ -352,10 +368,22 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
"RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n",
mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
sta_info_destroy_addr(sdata, mgmt->sa);
- ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
+ sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
rcu_read_unlock();
/*
+ * if we have any problem in allocating the new station, we reply with a
+ * DEAUTH frame to tell the other end that we had a problem
+ */
+ if (!sta) {
+ ieee80211_send_deauth_disassoc(sdata, sdata->u.ibss.bssid,
+ IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_UNSPECIFIED, true,
+ deauth_frame_buf);
+ return;
+ }
+
+ /*
* IEEE 802.11 standard does not require authentication in IBSS
* networks and most implementations do not seem to use it.
* However, try to reply to authentication attempts if someone
@@ -902,6 +930,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
case IEEE80211_STYPE_AUTH:
ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
break;
+ case IEEE80211_STYPE_DEAUTH:
+ ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
+ break;
}
mgmt_out:
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 204bfedba306..8c804550465b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -68,6 +68,8 @@ struct ieee80211_local;
#define IEEE80211_DEFAULT_MAX_SP_LEN \
IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL
+#define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */)
+
struct ieee80211_fragment_entry {
unsigned long first_frag_time;
unsigned int seq;
@@ -411,6 +413,7 @@ struct ieee80211_if_managed {
struct work_struct monitor_work;
struct work_struct chswitch_work;
struct work_struct beacon_connection_loss_work;
+ struct work_struct csa_connection_drop_work;
unsigned long beacon_timeout;
unsigned long probe_timeout;
@@ -970,7 +973,6 @@ struct ieee80211_local {
int scan_channel_idx;
int scan_ies_len;
- struct ieee80211_sched_scan_ies sched_scan_ies;
struct work_struct sched_scan_stopped_work;
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
@@ -1057,7 +1059,7 @@ struct ieee80211_local {
bool disable_dynamic_ps;
int user_power_level; /* in dBm */
- int power_constr_level; /* in dBm */
+ int ap_power_level; /* in dBm */
enum ieee80211_smps_mode smps_mode;
@@ -1165,7 +1167,6 @@ struct ieee802_11_elems {
u8 prep_len;
u8 perr_len;
u8 country_elem_len;
- u8 pwr_constr_elem_len;
u8 quiet_elem_len;
u8 num_of_quiet_elem; /* can be more the one */
u8 timeout_int_len;
@@ -1367,7 +1368,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
int ieee80211_reconfig(struct ieee80211_local *local);
void ieee80211_stop_device(struct ieee80211_local *local);
-#ifdef CONFIG_PM
int __ieee80211_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan);
@@ -1381,18 +1381,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
return ieee80211_reconfig(hw_to_local(hw));
}
-#else
-static inline int __ieee80211_suspend(struct ieee80211_hw *hw,
- struct cfg80211_wowlan *wowlan)
-{
- return 0;
-}
-
-static inline int __ieee80211_resume(struct ieee80211_hw *hw)
-{
- return 0;
-}
-#endif
/* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */
@@ -1459,6 +1447,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg,
u8 *extra, size_t extra_len, const u8 *bssid,
const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, u16 stype, u16 reason,
+ bool send_frame, u8 *frame_buf);
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index d747da541747..6f8a73c64fb3 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -793,11 +793,20 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
flush_work(&sdata->work);
/*
* When we get here, the interface is marked down.
- * Call synchronize_rcu() to wait for the RX path
+ * Call rcu_barrier() to wait both for the RX path
* should it be using the interface and enqueuing
- * frames at this very time on another CPU.
+ * frames at this very time on another CPU, and
+ * for the sta free call_rcu callbacks.
*/
- synchronize_rcu();
+ rcu_barrier();
+
+ /*
+ * free_sta_rcu() enqueues a work for the actual
+ * sta cleanup, so we need to flush it while
+ * sdata is still valid.
+ */
+ flush_workqueue(local->workqueue);
+
skb_queue_purge(&sdata->skb_queue);
/*
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 7ae678ba5d67..d27e61aaa71b 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -402,7 +402,7 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
* Synchronize so the TX path can no longer be using
* this key before we free/remove it.
*/
- synchronize_rcu();
+ synchronize_net();
if (key->local)
ieee80211_key_disable_hw_accel(key);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index bd7529363193..c80c4490351c 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -150,13 +150,11 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
- test_bit(SCAN_HW_SCANNING, &local->scanning))
+ test_bit(SCAN_HW_SCANNING, &local->scanning) ||
+ !local->ap_power_level)
power = chan->max_power;
else
- power = local->power_constr_level ?
- min(chan->max_power,
- (chan->max_reg_power - local->power_constr_level)) :
- chan->max_power;
+ power = min(chan->max_power, local->ap_power_level);
if (local->user_power_level >= 0)
power = min(power, local->user_power_level);
@@ -366,9 +364,7 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, recalc_smps);
- mutex_lock(&local->iflist_mtx);
ieee80211_recalc_smps(local);
- mutex_unlock(&local->iflist_mtx);
}
#ifdef CONFIG_INET
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 9d7ad366ef09..3ab34d816897 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -537,7 +537,8 @@ int mesh_plink_open(struct sta_info *sta)
spin_lock_bh(&sta->lock);
get_random_bytes(&llid, 2);
sta->llid = llid;
- if (sta->plink_state != NL80211_PLINK_LISTEN) {
+ if (sta->plink_state != NL80211_PLINK_LISTEN &&
+ sta->plink_state != NL80211_PLINK_BLOCKED) {
spin_unlock_bh(&sta->lock);
return -EBUSY;
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 5d77650d4363..e714ed8bb198 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -88,8 +88,6 @@ MODULE_PARM_DESC(probe_wait_ms,
#define TMR_RUNNING_TIMER 0
#define TMR_RUNNING_CHANSW 1
-#define DEAUTH_DISASSOC_LEN (24 /* hdr */ + 2 /* reason */)
-
/*
* All cfg80211 functions have to be called outside a locked
* section so that they can acquire a lock themselves... This
@@ -574,46 +572,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
ieee80211_tx_skb(sdata, skb);
}
-static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
- const u8 *bssid, u16 stype,
- u16 reason, bool send_frame,
- u8 *frame_buf)
-{
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt = (void *)frame_buf;
-
- /* build frame */
- mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
- mgmt->duration = 0; /* initialize only */
- mgmt->seq_ctrl = 0; /* initialize only */
- memcpy(mgmt->da, bssid, ETH_ALEN);
- memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
- memcpy(mgmt->bssid, bssid, ETH_ALEN);
- /* u.deauth.reason_code == u.disassoc.reason_code */
- mgmt->u.deauth.reason_code = cpu_to_le16(reason);
-
- if (send_frame) {
- skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- DEAUTH_DISASSOC_LEN);
- if (!skb)
- return;
-
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- /* copy in frame */
- memcpy(skb_put(skb, DEAUTH_DISASSOC_LEN),
- mgmt, DEAUTH_DISASSOC_LEN);
-
- if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
- IEEE80211_SKB_CB(skb)->flags |=
- IEEE80211_TX_INTFL_DONT_ENCRYPT;
-
- ieee80211_tx_skb(sdata, skb);
- }
-}
-
void ieee80211_send_pspoll(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
@@ -730,16 +688,13 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
trace_api_chswitch_done(sdata, success);
if (!success) {
- /*
- * If the channel switch was not successful, stay
- * around on the old channel. We currently lack
- * good handling of this situation, possibly we
- * should just drop the association.
- */
- sdata->local->csa_channel = sdata->local->oper_channel;
+ sdata_info(sdata,
+ "driver channel switch failed, disconnecting\n");
+ ieee80211_queue_work(&sdata->local->hw,
+ &ifmgd->csa_connection_drop_work);
+ } else {
+ ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}
-
- ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}
EXPORT_SYMBOL(ieee80211_chswitch_done);
@@ -784,8 +739,14 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return;
new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
- if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
+ if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) {
+ sdata_info(sdata,
+ "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
+ ifmgd->associated->bssid, new_freq);
+ ieee80211_queue_work(&sdata->local->hw,
+ &ifmgd->csa_connection_drop_work);
return;
+ }
sdata->local->csa_channel = new_ch;
@@ -818,23 +779,71 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
}
static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
- u16 capab_info, u8 *pwr_constr_elem,
- u8 pwr_constr_elem_len)
+ struct ieee80211_channel *channel,
+ const u8 *country_ie, u8 country_ie_len,
+ const u8 *pwr_constr_elem)
{
- struct ieee80211_conf *conf = &sdata->local->hw.conf;
+ struct ieee80211_country_ie_triplet *triplet;
+ int chan = ieee80211_frequency_to_channel(channel->center_freq);
+ int i, chan_pwr, chan_increment, new_ap_level;
+ bool have_chan_pwr = false;
- if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
+ /* Invalid IE */
+ if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
return;
- /* Power constraint IE length should be 1 octet */
- if (pwr_constr_elem_len != 1)
- return;
+ triplet = (void *)(country_ie + 3);
+ country_ie_len -= 3;
- if ((*pwr_constr_elem <= conf->channel->max_reg_power) &&
- (*pwr_constr_elem != sdata->local->power_constr_level)) {
- sdata->local->power_constr_level = *pwr_constr_elem;
- ieee80211_hw_config(sdata->local, 0);
+ switch (channel->band) {
+ default:
+ WARN_ON_ONCE(1);
+ /* fall through */
+ case IEEE80211_BAND_2GHZ:
+ case IEEE80211_BAND_60GHZ:
+ chan_increment = 1;
+ break;
+ case IEEE80211_BAND_5GHZ:
+ chan_increment = 4;
+ break;
}
+
+ /* find channel */
+ while (country_ie_len >= 3) {
+ u8 first_channel = triplet->chans.first_channel;
+
+ if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID)
+ goto next;
+
+ for (i = 0; i < triplet->chans.num_channels; i++) {
+ if (first_channel + i * chan_increment == chan) {
+ have_chan_pwr = true;
+ chan_pwr = triplet->chans.max_power;
+ break;
+ }
+ }
+ if (have_chan_pwr)
+ break;
+
+ next:
+ triplet++;
+ country_ie_len -= 3;
+ }
+
+ if (!have_chan_pwr)
+ return;
+
+ new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
+
+ if (sdata->local->ap_power_level == new_ap_level)
+ return;
+
+ sdata_info(sdata,
+ "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
+ new_ap_level, chan_pwr, *pwr_constr_elem,
+ sdata->u.mgd.bssid);
+ sdata->local->ap_power_level = new_ap_level;
+ ieee80211_hw_config(sdata->local, 0);
}
void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
@@ -1339,9 +1348,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
mutex_lock(&local->iflist_mtx);
ieee80211_recalc_ps(local, -1);
- ieee80211_recalc_smps(local);
mutex_unlock(&local->iflist_mtx);
+ ieee80211_recalc_smps(local);
ieee80211_recalc_ps_vif(sdata);
netif_tx_start_all_queues(sdata->dev);
@@ -1390,7 +1399,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(sdata, ifmgd->bssid);
if (sta) {
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta, tx);
+ ieee80211_sta_tear_down_BA_sessions(sta, false);
}
mutex_unlock(&local->sta_mtx);
@@ -1438,7 +1447,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
- local->power_constr_level = 0;
+ local->ap_power_level = 0;
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
@@ -1692,11 +1701,12 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ap_probereq_get);
-static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
+static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
+ bool transmit_frame)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- u8 frame_buf[DEAUTH_DISASSOC_LEN];
+ u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
mutex_lock(&ifmgd->mtx);
if (!ifmgd->associated) {
@@ -1704,19 +1714,17 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
return;
}
- sdata_info(sdata, "Connection to AP %pM lost\n",
- ifmgd->associated->bssid);
-
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
- false, frame_buf);
+ transmit_frame, frame_buf);
+ ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
mutex_unlock(&ifmgd->mtx);
/*
* must be outside lock due to cfg80211,
* but that's not a problem.
*/
- cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+ cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
@@ -1739,10 +1747,24 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
rcu_read_unlock();
}
- if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
- __ieee80211_connection_loss(sdata);
- else
+ if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) {
+ sdata_info(sdata, "Connection to AP %pM lost\n",
+ ifmgd->bssid);
+ __ieee80211_disconnect(sdata, false);
+ } else {
ieee80211_mgd_probe_ap(sdata, true);
+ }
+}
+
+static void ieee80211_csa_connection_drop_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.csa_connection_drop_work);
+
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+ __ieee80211_disconnect(sdata, true);
}
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
@@ -2530,15 +2552,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
bssid, true);
}
- /* Note: country IE parsing is done for us by cfg80211 */
- if (elems.country_elem) {
- /* TODO: IBSS also needs this */
- if (elems.pwr_constr_elem)
- ieee80211_handle_pwr_constr(sdata,
- le16_to_cpu(mgmt->u.probe_resp.capab_info),
- elems.pwr_constr_elem,
- elems.pwr_constr_elem_len);
- }
+ if (elems.country_elem && elems.pwr_constr_elem &&
+ mgmt->u.probe_resp.capab_info &
+ cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
+ ieee80211_handle_pwr_constr(sdata, local->oper_channel,
+ elems.country_elem,
+ elems.country_elem_len,
+ elems.pwr_constr_elem);
ieee80211_bss_info_change_notify(sdata, changed);
}
@@ -2635,7 +2655,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- u8 frame_buf[DEAUTH_DISASSOC_LEN];
+ u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
false, frame_buf);
@@ -2645,7 +2665,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
* must be outside lock due to cfg80211,
* but that's not a problem.
*/
- cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+ cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
@@ -2929,6 +2949,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
cancel_work_sync(&ifmgd->monitor_work);
cancel_work_sync(&ifmgd->beacon_connection_loss_work);
+ cancel_work_sync(&ifmgd->csa_connection_drop_work);
if (del_timer_sync(&ifmgd->timer))
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
@@ -2985,6 +3006,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
INIT_WORK(&ifmgd->beacon_connection_loss_work,
ieee80211_beacon_connection_loss_work);
+ INIT_WORK(&ifmgd->csa_connection_drop_work,
+ ieee80211_csa_connection_drop_work);
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata);
@@ -3525,7 +3548,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct cfg80211_deauth_request *req)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- u8 frame_buf[DEAUTH_DISASSOC_LEN];
+ u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
mutex_lock(&ifmgd->mtx);
@@ -3553,7 +3576,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&ifmgd->mtx);
- __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+ __cfg80211_send_deauth(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
@@ -3567,7 +3591,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 bssid[ETH_ALEN];
- u8 frame_buf[DEAUTH_DISASSOC_LEN];
+ u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
mutex_lock(&ifmgd->mtx);
@@ -3592,7 +3616,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
frame_buf);
mutex_unlock(&ifmgd->mtx);
- __cfg80211_send_disassoc(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+ __cfg80211_send_disassoc(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 507121dad082..83608ac16780 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -233,8 +233,7 @@ static void ieee80211_hw_roc_start(struct work_struct *work)
u32 dur = dep->duration;
dep->duration = dur - roc->duration;
roc->duration = dur;
- list_del(&dep->list);
- list_add(&dep->list, &roc->list);
+ list_move(&dep->list, &roc->list);
}
}
out_unlock:
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 740e414d44f4..c4cdbde24fd3 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -407,7 +407,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
enum ieee80211_band band = local->hw.conf.channel->band;
sdata = rcu_dereference_protected(local->scan_sdata,
- lockdep_is_held(&local->mtx));;
+ lockdep_is_held(&local->mtx));
for (i = 0; i < local->scan_req->n_ssids; i++)
ieee80211_send_probe_req(
@@ -917,6 +917,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
struct cfg80211_sched_scan_request *req)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sched_scan_ies sched_scan_ies;
int ret, i;
mutex_lock(&local->mtx);
@@ -935,33 +936,28 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
if (!local->hw.wiphy->bands[i])
continue;
- local->sched_scan_ies.ie[i] = kzalloc(2 +
- IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len +
- req->ie_len,
- GFP_KERNEL);
- if (!local->sched_scan_ies.ie[i]) {
+ sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN +
+ local->scan_ies_len +
+ req->ie_len,
+ GFP_KERNEL);
+ if (!sched_scan_ies.ie[i]) {
ret = -ENOMEM;
goto out_free;
}
- local->sched_scan_ies.len[i] =
- ieee80211_build_preq_ies(local,
- local->sched_scan_ies.ie[i],
+ sched_scan_ies.len[i] =
+ ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
req->ie, req->ie_len, i,
(u32) -1, 0);
}
- ret = drv_sched_scan_start(local, sdata, req,
- &local->sched_scan_ies);
- if (ret == 0) {
+ ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
+ if (ret == 0)
rcu_assign_pointer(local->sched_scan_sdata, sdata);
- goto out;
- }
out_free:
while (i > 0)
- kfree(local->sched_scan_ies.ie[--i]);
+ kfree(sched_scan_ies.ie[--i]);
out:
mutex_unlock(&local->mtx);
return ret;
@@ -970,7 +966,7 @@ out:
int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- int ret = 0, i;
+ int ret = 0;
mutex_lock(&local->mtx);
@@ -979,12 +975,9 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
goto out;
}
- if (rcu_access_pointer(local->sched_scan_sdata)) {
- for (i = 0; i < IEEE80211_NUM_BANDS; i++)
- kfree(local->sched_scan_ies.ie[i]);
-
+ if (rcu_access_pointer(local->sched_scan_sdata))
drv_sched_scan_stop(local, sdata);
- }
+
out:
mutex_unlock(&local->mtx);
@@ -1006,7 +999,6 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
struct ieee80211_local *local =
container_of(work, struct ieee80211_local,
sched_scan_stopped_work);
- int i;
mutex_lock(&local->mtx);
@@ -1015,9 +1007,6 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
return;
}
- for (i = 0; i < IEEE80211_NUM_BANDS; i++)
- kfree(local->sched_scan_ies.ie[i]);
-
rcu_assign_pointer(local->sched_scan_sdata, NULL);
mutex_unlock(&local->mtx);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 06fa75ceb025..797dd36a220d 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -91,6 +91,70 @@ static int sta_info_hash_del(struct ieee80211_local *local,
return -ENOENT;
}
+static void free_sta_work(struct work_struct *wk)
+{
+ struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
+ int ac, i;
+ struct tid_ampdu_tx *tid_tx;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+
+ /*
+ * At this point, when being called as call_rcu callback,
+ * neither mac80211 nor the driver can reference this
+ * sta struct any more except by still existing timers
+ * associated with this station that we clean up below.
+ */
+
+ if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
+ BUG_ON(!sdata->bss);
+
+ clear_sta_flag(sta, WLAN_STA_PS_STA);
+
+ atomic_dec(&sdata->bss->num_sta_ps);
+ sta_info_recalc_tim(sta);
+ }
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
+ __skb_queue_purge(&sta->ps_tx_buf[ac]);
+ __skb_queue_purge(&sta->tx_filtered[ac]);
+ }
+
+#ifdef CONFIG_MAC80211_MESH
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ mesh_accept_plinks_update(sdata);
+ mesh_plink_deactivate(sta);
+ del_timer_sync(&sta->plink_timer);
+ }
+#endif
+
+ cancel_work_sync(&sta->drv_unblock_wk);
+
+ /*
+ * Destroy aggregation state here. It would be nice to wait for the
+ * driver to finish aggregation stop and then clean up, but for now
+ * drivers have to handle aggregation stop being requested, followed
+ * directly by station destruction.
+ */
+ for (i = 0; i < STA_TID_NUM; i++) {
+ tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
+ if (!tid_tx)
+ continue;
+ __skb_queue_purge(&tid_tx->pending);
+ kfree(tid_tx);
+ }
+
+ sta_info_free(local, sta);
+}
+
+static void free_sta_rcu(struct rcu_head *h)
+{
+ struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
+
+ ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
+}
+
/* protected by RCU */
struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
const u8 *addr)
@@ -241,6 +305,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
spin_lock_init(&sta->lock);
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
+ INIT_WORK(&sta->free_sta_wk, free_sta_work);
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx);
@@ -654,8 +719,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
{
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
- int ret, i, ac;
- struct tid_ampdu_tx *tid_tx;
+ int ret, i;
might_sleep();
@@ -674,7 +738,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
* will be sufficient.
*/
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta, true);
+ ieee80211_sta_tear_down_BA_sessions(sta, false);
ret = sta_info_hash_del(local, sta);
if (ret)
@@ -711,65 +775,14 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
WARN_ON_ONCE(ret != 0);
}
- /*
- * At this point, after we wait for an RCU grace period,
- * neither mac80211 nor the driver can reference this
- * sta struct any more except by still existing timers
- * associated with this station that we clean up below.
- */
- synchronize_rcu();
-
- if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
- BUG_ON(!sdata->bss);
-
- clear_sta_flag(sta, WLAN_STA_PS_STA);
-
- atomic_dec(&sdata->bss->num_sta_ps);
- sta_info_recalc_tim(sta);
- }
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
- local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
- __skb_queue_purge(&sta->ps_tx_buf[ac]);
- __skb_queue_purge(&sta->tx_filtered[ac]);
- }
-
-#ifdef CONFIG_MAC80211_MESH
- if (ieee80211_vif_is_mesh(&sdata->vif))
- mesh_accept_plinks_update(sdata);
-#endif
-
sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
- cancel_work_sync(&sta->drv_unblock_wk);
-
cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL);
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
-#ifdef CONFIG_MAC80211_MESH
- if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
- mesh_plink_deactivate(sta);
- del_timer_sync(&sta->plink_timer);
- }
-#endif
-
- /*
- * Destroy aggregation state here. It would be nice to wait for the
- * driver to finish aggregation stop and then clean up, but for now
- * drivers have to handle aggregation stop being requested, followed
- * directly by station destruction.
- */
- for (i = 0; i < STA_TID_NUM; i++) {
- tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
- if (!tid_tx)
- continue;
- __skb_queue_purge(&tid_tx->pending);
- kfree(tid_tx);
- }
-
- sta_info_free(local, sta);
+ call_rcu(&sta->rcu_head, free_sta_rcu);
return 0;
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index a470e1123a55..c88f161f8118 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -287,6 +287,7 @@ struct sta_ampdu_mlme {
struct sta_info {
/* General information, mostly static */
struct list_head list;
+ struct rcu_head rcu_head;
struct sta_info __rcu *hnext;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
@@ -297,6 +298,7 @@ struct sta_info {
spinlock_t lock;
struct work_struct drv_unblock_wk;
+ struct work_struct free_sta_wk;
u16 listen_interval;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index b0801b7d572d..2ce89732d0f2 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -517,29 +517,41 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
u64 cookie = (unsigned long)skb;
+ bool found = false;
+
acked = info->flags & IEEE80211_TX_STAT_ACK;
- if (ieee80211_is_nullfunc(hdr->frame_control) ||
- ieee80211_is_qos_nullfunc(hdr->frame_control)) {
- cfg80211_probe_status(skb->dev, hdr->addr1,
- cookie, acked, GFP_ATOMIC);
- } else if (skb->dev) {
- cfg80211_mgmt_tx_status(
- skb->dev->ieee80211_ptr, cookie, skb->data,
- skb->len, acked, GFP_ATOMIC);
- } else {
- struct ieee80211_sub_if_data *p2p_sdata;
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
- rcu_read_lock();
+ if (skb->dev != sdata->dev)
+ continue;
- p2p_sdata = rcu_dereference(local->p2p_sdata);
- if (p2p_sdata) {
- cfg80211_mgmt_tx_status(
- &p2p_sdata->wdev, cookie, skb->data,
- skb->len, acked, GFP_ATOMIC);
- }
- rcu_read_unlock();
+ found = true;
+ break;
+ }
+
+ if (!skb->dev) {
+ sdata = rcu_dereference(local->p2p_sdata);
+ if (sdata)
+ found = true;
+ }
+
+ if (!found)
+ skb->dev = NULL;
+ else if (ieee80211_is_nullfunc(hdr->frame_control) ||
+ ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+ cfg80211_probe_status(sdata->dev, hdr->addr1,
+ cookie, acked, GFP_ATOMIC);
+ } else {
+ cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
+ skb->len, acked, GFP_ATOMIC);
}
+
+ rcu_read_unlock();
}
if (unlikely(info->ack_frame_id)) {
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 29eb4e678235..e0e0d1d0e830 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -580,7 +580,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
tx->key = NULL;
else
skip_hw = (tx->key->conf.flags &
- IEEE80211_KEY_FLAG_SW_MGMT) &&
+ IEEE80211_KEY_FLAG_SW_MGMT_TX) &&
ieee80211_is_mgmt(hdr->frame_control);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 471fb0516c99..22ca35054dd0 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -792,8 +792,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
elems->country_elem_len = elen;
break;
case WLAN_EID_PWR_CONSTRAINT:
+ if (elen != 1) {
+ elem_parse_failed = true;
+ break;
+ }
elems->pwr_constr_elem = pos;
- elems->pwr_constr_elem_len = elen;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
elems->timeout_int = pos;
@@ -1004,6 +1007,45 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_skb(sdata, skb);
}
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, u16 stype, u16 reason,
+ bool send_frame, u8 *frame_buf)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt = (void *)frame_buf;
+
+ /* build frame */
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+ mgmt->duration = 0; /* initialize only */
+ mgmt->seq_ctrl = 0; /* initialize only */
+ memcpy(mgmt->da, bssid, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
+ /* u.deauth.reason_code == u.disassoc.reason_code */
+ mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+
+ if (send_frame) {
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+ IEEE80211_DEAUTH_FRAME_LEN);
+ if (!skb)
+ return;
+
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ /* copy in frame */
+ memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN),
+ mgmt, IEEE80211_DEAUTH_FRAME_LEN);
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+ !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
+ IEEE80211_SKB_CB(skb)->flags |=
+ IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
+ ieee80211_tx_skb(sdata, skb);
+ }
+}
+
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
@@ -1564,14 +1606,13 @@ static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
return 0;
}
-/* must hold iflist_mtx */
void ieee80211_recalc_smps(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
int count = 0;
- lockdep_assert_held(&local->iflist_mtx);
+ mutex_lock(&local->iflist_mtx);
/*
* This function could be improved to handle multiple
@@ -1600,12 +1641,14 @@ void ieee80211_recalc_smps(struct ieee80211_local *local)
}
if (smps_mode == local->smps_mode)
- return;
+ goto unlock;
set:
local->smps_mode = smps_mode;
/* changed flag is auto-detected for this */
ieee80211_hw_config(local, 0);
+ unlock:
+ mutex_unlock(&local->iflist_mtx);
}
static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
diff --git a/net/nfc/core.c b/net/nfc/core.c
index ff749794bc5b..c9eacc1f145f 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -679,7 +679,7 @@ static void nfc_release(struct device *d)
if (dev->ops->check_presence) {
del_timer_sync(&dev->check_pres_timer);
- destroy_workqueue(dev->check_pres_wq);
+ cancel_work_sync(&dev->check_pres_work);
}
nfc_genl_data_exit(&dev->genl_data);
@@ -715,7 +715,7 @@ static void nfc_check_pres_timeout(unsigned long data)
{
struct nfc_dev *dev = (struct nfc_dev *)data;
- queue_work(dev->check_pres_wq, &dev->check_pres_work);
+ queue_work(system_nrt_wq, &dev->check_pres_work);
}
struct class nfc_class = {
@@ -784,20 +784,11 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
dev->targets_generation = 1;
if (ops->check_presence) {
- char name[32];
init_timer(&dev->check_pres_timer);
dev->check_pres_timer.data = (unsigned long)dev;
dev->check_pres_timer.function = nfc_check_pres_timeout;
INIT_WORK(&dev->check_pres_work, nfc_check_pres_work);
- snprintf(name, sizeof(name), "nfc%d_check_pres_wq", dev->idx);
- dev->check_pres_wq = alloc_workqueue(name, WQ_NON_REENTRANT |
- WQ_UNBOUND |
- WQ_MEM_RECLAIM, 1);
- if (dev->check_pres_wq == NULL) {
- kfree(dev);
- return NULL;
- }
}
return dev;
diff --git a/net/nfc/hci/Makefile b/net/nfc/hci/Makefile
index f9c44b2fb065..c5dbb6891b24 100644
--- a/net/nfc/hci/Makefile
+++ b/net/nfc/hci/Makefile
@@ -4,5 +4,5 @@
obj-$(CONFIG_NFC_HCI) += hci.o
-hci-y := core.o hcp.o command.o
-hci-$(CONFIG_NFC_SHDLC) += shdlc.o
+hci-y := core.o hcp.o command.o llc.o llc_nop.o
+hci-$(CONFIG_NFC_SHDLC) += llc_shdlc.o
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 46362ef979db..71c6a7086b8f 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -28,10 +28,29 @@
#include "hci.h"
-static void nfc_hci_execute_cb(struct nfc_hci_dev *hdev, int err,
- struct sk_buff *skb, void *cb_data)
+static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+ const u8 *param, size_t param_len,
+ data_exchange_cb_t cb, void *cb_context)
{
- struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)cb_data;
+ pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe,
+ cmd, param_len);
+
+ /* TODO: Define hci cmd execution delay. Should it be the same
+ * for all commands?
+ */
+ return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
+ param, param_len, cb, cb_context, 3000);
+}
+
+/*
+ * HCI command execution completion callback.
+ * err will be a standard linux error (may be converted from HCI response)
+ * skb contains the response data and must be disposed, or may be NULL if
+ * an error occured
+ */
+static void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err)
+{
+ struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context;
pr_debug("HCI Cmd completed with result=%d\n", err);
@@ -55,7 +74,8 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
hcp_ew.exec_complete = false;
hcp_ew.result_skb = NULL;
- pr_debug("through pipe=%d, cmd=%d, plen=%zd\n", pipe, cmd, param_len);
+ pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe,
+ cmd, param_len);
/* TODO: Define hci cmd execution delay. Should it be the same
* for all commands?
@@ -133,6 +153,23 @@ int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
}
EXPORT_SYMBOL(nfc_hci_send_cmd);
+int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+ const u8 *param, size_t param_len,
+ data_exchange_cb_t cb, void *cb_context)
+{
+ u8 pipe;
+
+ pr_debug("\n");
+
+ pipe = hdev->gate2pipe[gate];
+ if (pipe == NFC_HCI_INVALID_PIPE)
+ return -EADDRNOTAVAIL;
+
+ return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len,
+ cb, cb_context);
+}
+EXPORT_SYMBOL(nfc_hci_send_cmd_async);
+
int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
const u8 *param, size_t param_len)
{
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 1ac7b3fac6c9..d378d93de62e 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -26,6 +26,7 @@
#include <net/nfc/nfc.h>
#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
#include "hci.h"
@@ -57,12 +58,11 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
if (hdev->cmd_pending_msg) {
if (timer_pending(&hdev->cmd_timer) == 0) {
if (hdev->cmd_pending_msg->cb)
- hdev->cmd_pending_msg->cb(hdev,
- -ETIME,
- NULL,
- hdev->
+ hdev->cmd_pending_msg->cb(hdev->
cmd_pending_msg->
- cb_context);
+ cb_context,
+ NULL,
+ -ETIME);
kfree(hdev->cmd_pending_msg);
hdev->cmd_pending_msg = NULL;
} else
@@ -78,12 +78,12 @@ next_msg:
pr_debug("msg_tx_queue has a cmd to send\n");
while ((skb = skb_dequeue(&msg->msg_frags)) != NULL) {
- r = hdev->ops->xmit(hdev, skb);
+ r = nfc_llc_xmit_from_hci(hdev->llc, skb);
if (r < 0) {
kfree_skb(skb);
skb_queue_purge(&msg->msg_frags);
if (msg->cb)
- msg->cb(hdev, r, NULL, msg->cb_context);
+ msg->cb(msg->cb_context, NULL, r);
kfree(msg);
break;
}
@@ -133,15 +133,15 @@ static void __nfc_hci_cmd_completion(struct nfc_hci_dev *hdev, int err,
del_timer_sync(&hdev->cmd_timer);
if (hdev->cmd_pending_msg->cb)
- hdev->cmd_pending_msg->cb(hdev, err, skb,
- hdev->cmd_pending_msg->cb_context);
+ hdev->cmd_pending_msg->cb(hdev->cmd_pending_msg->cb_context,
+ skb, err);
else
kfree_skb(skb);
kfree(hdev->cmd_pending_msg);
hdev->cmd_pending_msg = NULL;
- queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+ queue_work(system_nrt_wq, &hdev->msg_tx_work);
}
void nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result,
@@ -326,7 +326,7 @@ static void nfc_hci_cmd_timeout(unsigned long data)
{
struct nfc_hci_dev *hdev = (struct nfc_hci_dev *)data;
- queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+ queue_work(system_nrt_wq, &hdev->msg_tx_work);
}
static int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count,
@@ -398,8 +398,7 @@ disconnect_all:
nfc_hci_disconnect_all_gates(hdev);
exit:
- if (skb)
- kfree_skb(skb);
+ kfree_skb(skb);
return r;
}
@@ -470,29 +469,38 @@ static int hci_dev_up(struct nfc_dev *nfc_dev)
return r;
}
+ r = nfc_llc_start(hdev->llc);
+ if (r < 0)
+ goto exit_close;
+
r = hci_dev_session_init(hdev);
if (r < 0)
- goto exit;
+ goto exit_llc;
r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
NFC_HCI_EVT_END_OPERATION, NULL, 0);
if (r < 0)
- goto exit;
+ goto exit_llc;
if (hdev->ops->hci_ready) {
r = hdev->ops->hci_ready(hdev);
if (r < 0)
- goto exit;
+ goto exit_llc;
}
r = hci_dev_version(hdev);
if (r < 0)
- goto exit;
+ goto exit_llc;
+
+ return 0;
+
+exit_llc:
+ nfc_llc_stop(hdev->llc);
+
+exit_close:
+ if (hdev->ops->close)
+ hdev->ops->close(hdev);
-exit:
- if (r < 0)
- if (hdev->ops->close)
- hdev->ops->close(hdev);
return r;
}
@@ -500,6 +508,8 @@ static int hci_dev_down(struct nfc_dev *nfc_dev)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+ nfc_llc_stop(hdev->llc);
+
if (hdev->ops->close)
hdev->ops->close(hdev);
@@ -539,13 +549,37 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev,
{
}
+#define HCI_CB_TYPE_TRANSCEIVE 1
+
+static void hci_transceive_cb(void *context, struct sk_buff *skb, int err)
+{
+ struct nfc_hci_dev *hdev = context;
+
+ switch (hdev->async_cb_type) {
+ case HCI_CB_TYPE_TRANSCEIVE:
+ /*
+ * TODO: Check RF Error indicator to make sure data is valid.
+ * It seems that HCI cmd can complete without error, but data
+ * can be invalid if an RF error occured? Ignore for now.
+ */
+ if (err == 0)
+ skb_trim(skb, skb->len - 1); /* RF Err ind */
+
+ hdev->async_cb(hdev->async_cb_context, skb, err);
+ break;
+ default:
+ if (err == 0)
+ kfree_skb(skb);
+ break;
+ }
+}
+
static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
struct sk_buff *skb, data_exchange_cb_t cb,
void *cb_context)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
int r;
- struct sk_buff *res_skb = NULL;
pr_debug("target_idx=%d\n", target->idx);
@@ -553,40 +587,37 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
case NFC_HCI_RF_READER_A_GATE:
case NFC_HCI_RF_READER_B_GATE:
if (hdev->ops->data_exchange) {
- r = hdev->ops->data_exchange(hdev, target, skb,
- &res_skb);
+ r = hdev->ops->data_exchange(hdev, target, skb, cb,
+ cb_context);
if (r <= 0) /* handled */
break;
}
*skb_push(skb, 1) = 0; /* CTR, see spec:10.2.2.1 */
- r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
- NFC_HCI_WR_XCHG_DATA,
- skb->data, skb->len, &res_skb);
- /*
- * TODO: Check RF Error indicator to make sure data is valid.
- * It seems that HCI cmd can complete without error, but data
- * can be invalid if an RF error occured? Ignore for now.
- */
- if (r == 0)
- skb_trim(res_skb, res_skb->len - 1); /* RF Err ind */
+
+ hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE;
+ hdev->async_cb = cb;
+ hdev->async_cb_context = cb_context;
+
+ r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ NFC_HCI_WR_XCHG_DATA, skb->data,
+ skb->len, hci_transceive_cb, hdev);
break;
default:
if (hdev->ops->data_exchange) {
- r = hdev->ops->data_exchange(hdev, target, skb,
- &res_skb);
+ r = hdev->ops->data_exchange(hdev, target, skb, cb,
+ cb_context);
if (r == 1)
r = -ENOTSUPP;
}
else
r = -ENOTSUPP;
+ break;
}
kfree_skb(skb);
- cb(cb_context, res_skb, r);
-
- return 0;
+ return r;
}
static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -600,6 +631,93 @@ static int hci_check_presence(struct nfc_dev *nfc_dev,
return 0;
}
+static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
+{
+ mutex_lock(&hdev->msg_tx_mutex);
+
+ if (hdev->cmd_pending_msg == NULL) {
+ nfc_driver_failure(hdev->ndev, err);
+ goto exit;
+ }
+
+ __nfc_hci_cmd_completion(hdev, err, NULL);
+
+exit:
+ mutex_unlock(&hdev->msg_tx_mutex);
+}
+
+static void nfc_hci_llc_failure(struct nfc_hci_dev *hdev, int err)
+{
+ nfc_hci_failure(hdev, err);
+}
+
+static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hcp_packet *packet;
+ u8 type;
+ u8 instruction;
+ struct sk_buff *hcp_skb;
+ u8 pipe;
+ struct sk_buff *frag_skb;
+ int msg_len;
+
+ packet = (struct hcp_packet *)skb->data;
+ if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) {
+ skb_queue_tail(&hdev->rx_hcp_frags, skb);
+ return;
+ }
+
+ /* it's the last fragment. Does it need re-aggregation? */
+ if (skb_queue_len(&hdev->rx_hcp_frags)) {
+ pipe = packet->header & NFC_HCI_FRAGMENT;
+ skb_queue_tail(&hdev->rx_hcp_frags, skb);
+
+ msg_len = 0;
+ skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
+ msg_len += (frag_skb->len -
+ NFC_HCI_HCP_PACKET_HEADER_LEN);
+ }
+
+ hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN +
+ msg_len, GFP_KERNEL);
+ if (hcp_skb == NULL) {
+ nfc_hci_failure(hdev, -ENOMEM);
+ return;
+ }
+
+ *skb_put(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN) = pipe;
+
+ skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
+ msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN;
+ memcpy(skb_put(hcp_skb, msg_len),
+ frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN,
+ msg_len);
+ }
+
+ skb_queue_purge(&hdev->rx_hcp_frags);
+ } else {
+ packet->header &= NFC_HCI_FRAGMENT;
+ hcp_skb = skb;
+ }
+
+ /* if this is a response, dispatch immediately to
+ * unblock waiting cmd context. Otherwise, enqueue to dispatch
+ * in separate context where handler can also execute command.
+ */
+ packet = (struct hcp_packet *)hcp_skb->data;
+ type = HCP_MSG_GET_TYPE(packet->message.header);
+ if (type == NFC_HCI_HCP_RESPONSE) {
+ pipe = packet->header;
+ instruction = HCP_MSG_GET_CMD(packet->message.header);
+ skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN +
+ NFC_HCI_HCP_MESSAGE_HEADER_LEN);
+ nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
+ } else {
+ skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
+ queue_work(system_nrt_wq, &hdev->msg_rx_work);
+ }
+}
+
static struct nfc_ops hci_nfc_ops = {
.dev_up = hci_dev_up,
.dev_down = hci_dev_down,
@@ -614,6 +732,7 @@ static struct nfc_ops hci_nfc_ops = {
struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
struct nfc_hci_init_data *init_data,
u32 protocols,
+ const char *llc_name,
int tx_headroom,
int tx_tailroom,
int max_link_payload)
@@ -630,10 +749,19 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
if (hdev == NULL)
return NULL;
+ hdev->llc = nfc_llc_allocate(llc_name, hdev, ops->xmit,
+ nfc_hci_recv_from_llc, tx_headroom,
+ tx_tailroom, nfc_hci_llc_failure);
+ if (hdev->llc == NULL) {
+ kfree(hdev);
+ return NULL;
+ }
+
hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
tx_headroom + HCI_CMDS_HEADROOM,
tx_tailroom);
if (!hdev->ndev) {
+ nfc_llc_free(hdev->llc);
kfree(hdev);
return NULL;
}
@@ -653,29 +781,18 @@ EXPORT_SYMBOL(nfc_hci_allocate_device);
void nfc_hci_free_device(struct nfc_hci_dev *hdev)
{
nfc_free_device(hdev->ndev);
+ nfc_llc_free(hdev->llc);
kfree(hdev);
}
EXPORT_SYMBOL(nfc_hci_free_device);
int nfc_hci_register_device(struct nfc_hci_dev *hdev)
{
- struct device *dev = &hdev->ndev->dev;
- const char *devname = dev_name(dev);
- char name[32];
- int r = 0;
-
mutex_init(&hdev->msg_tx_mutex);
INIT_LIST_HEAD(&hdev->msg_tx_queue);
INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work);
- snprintf(name, sizeof(name), "%s_hci_msg_tx_wq", devname);
- hdev->msg_tx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
- WQ_MEM_RECLAIM, 1);
- if (hdev->msg_tx_wq == NULL) {
- r = -ENOMEM;
- goto exit;
- }
init_timer(&hdev->cmd_timer);
hdev->cmd_timer.data = (unsigned long)hdev;
@@ -684,27 +801,10 @@ int nfc_hci_register_device(struct nfc_hci_dev *hdev)
skb_queue_head_init(&hdev->rx_hcp_frags);
INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work);
- snprintf(name, sizeof(name), "%s_hci_msg_rx_wq", devname);
- hdev->msg_rx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
- WQ_MEM_RECLAIM, 1);
- if (hdev->msg_rx_wq == NULL) {
- r = -ENOMEM;
- goto exit;
- }
skb_queue_head_init(&hdev->msg_rx_queue);
- r = nfc_register_device(hdev->ndev);
-
-exit:
- if (r < 0) {
- if (hdev->msg_tx_wq)
- destroy_workqueue(hdev->msg_tx_wq);
- if (hdev->msg_rx_wq)
- destroy_workqueue(hdev->msg_rx_wq);
- }
-
- return r;
+ return nfc_register_device(hdev->ndev);
}
EXPORT_SYMBOL(nfc_hci_register_device);
@@ -725,9 +825,8 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
nfc_unregister_device(hdev->ndev);
- destroy_workqueue(hdev->msg_tx_wq);
-
- destroy_workqueue(hdev->msg_rx_wq);
+ cancel_work_sync(&hdev->msg_tx_work);
+ cancel_work_sync(&hdev->msg_rx_work);
}
EXPORT_SYMBOL(nfc_hci_unregister_device);
@@ -743,93 +842,30 @@ void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev)
}
EXPORT_SYMBOL(nfc_hci_get_clientdata);
-static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
-{
- mutex_lock(&hdev->msg_tx_mutex);
-
- if (hdev->cmd_pending_msg == NULL) {
- nfc_driver_failure(hdev->ndev, err);
- goto exit;
- }
-
- __nfc_hci_cmd_completion(hdev, err, NULL);
-
-exit:
- mutex_unlock(&hdev->msg_tx_mutex);
-}
-
void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
{
nfc_hci_failure(hdev, err);
}
EXPORT_SYMBOL(nfc_hci_driver_failure);
-void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
{
- struct hcp_packet *packet;
- u8 type;
- u8 instruction;
- struct sk_buff *hcp_skb;
- u8 pipe;
- struct sk_buff *frag_skb;
- int msg_len;
-
- packet = (struct hcp_packet *)skb->data;
- if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) {
- skb_queue_tail(&hdev->rx_hcp_frags, skb);
- return;
- }
-
- /* it's the last fragment. Does it need re-aggregation? */
- if (skb_queue_len(&hdev->rx_hcp_frags)) {
- pipe = packet->header & NFC_HCI_FRAGMENT;
- skb_queue_tail(&hdev->rx_hcp_frags, skb);
-
- msg_len = 0;
- skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
- msg_len += (frag_skb->len -
- NFC_HCI_HCP_PACKET_HEADER_LEN);
- }
-
- hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN +
- msg_len, GFP_KERNEL);
- if (hcp_skb == NULL) {
- nfc_hci_failure(hdev, -ENOMEM);
- return;
- }
-
- *skb_put(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN) = pipe;
-
- skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
- msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN;
- memcpy(skb_put(hcp_skb, msg_len),
- frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN,
- msg_len);
- }
+ nfc_llc_rcv_from_drv(hdev->llc, skb);
+}
+EXPORT_SYMBOL(nfc_hci_recv_frame);
- skb_queue_purge(&hdev->rx_hcp_frags);
- } else {
- packet->header &= NFC_HCI_FRAGMENT;
- hcp_skb = skb;
- }
+static int __init nfc_hci_init(void)
+{
+ return nfc_llc_init();
+}
- /* if this is a response, dispatch immediately to
- * unblock waiting cmd context. Otherwise, enqueue to dispatch
- * in separate context where handler can also execute command.
- */
- packet = (struct hcp_packet *)hcp_skb->data;
- type = HCP_MSG_GET_TYPE(packet->message.header);
- if (type == NFC_HCI_HCP_RESPONSE) {
- pipe = packet->header;
- instruction = HCP_MSG_GET_CMD(packet->message.header);
- skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN +
- NFC_HCI_HCP_MESSAGE_HEADER_LEN);
- nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
- } else {
- skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
- queue_work(hdev->msg_rx_wq, &hdev->msg_rx_work);
- }
+static void __exit nfc_hci_exit(void)
+{
+ nfc_llc_exit();
}
-EXPORT_SYMBOL(nfc_hci_recv_frame);
+
+subsys_initcall(nfc_hci_init);
+module_exit(nfc_hci_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NFC HCI Core");
diff --git a/net/nfc/hci/hci.h b/net/nfc/hci/hci.h
index fa9a21e92239..b274d12c18ac 100644
--- a/net/nfc/hci/hci.h
+++ b/net/nfc/hci/hci.h
@@ -20,6 +20,8 @@
#ifndef __LOCAL_HCI_H
#define __LOCAL_HCI_H
+#include <net/nfc/hci.h>
+
struct gate_pipe_map {
u8 gate;
u8 pipe;
@@ -35,15 +37,6 @@ struct hcp_packet {
struct hcp_message message;
} __packed;
-/*
- * HCI command execution completion callback.
- * result will be a standard linux error (may be converted from HCI response)
- * skb contains the response data and must be disposed, or may be NULL if
- * an error occured
- */
-typedef void (*hci_cmd_cb_t) (struct nfc_hci_dev *hdev, int result,
- struct sk_buff *skb, void *cb_data);
-
struct hcp_exec_waiter {
wait_queue_head_t *wq;
bool exec_complete;
@@ -55,7 +48,7 @@ struct hci_msg {
struct list_head msg_l;
struct sk_buff_head msg_frags;
bool wait_response;
- hci_cmd_cb_t cb;
+ data_exchange_cb_t cb;
void *cb_context;
unsigned long completion_delay;
};
@@ -83,7 +76,7 @@ struct hci_create_pipe_resp {
int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
u8 type, u8 instruction,
const u8 *payload, size_t payload_len,
- hci_cmd_cb_t cb, void *cb_data,
+ data_exchange_cb_t cb, void *cb_context,
unsigned long completion_delay);
u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe);
diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c
index f4dad1a89740..208eedd07ee3 100644
--- a/net/nfc/hci/hcp.c
+++ b/net/nfc/hci/hcp.c
@@ -35,7 +35,7 @@
int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
u8 type, u8 instruction,
const u8 *payload, size_t payload_len,
- hci_cmd_cb_t cb, void *cb_data,
+ data_exchange_cb_t cb, void *cb_context,
unsigned long completion_delay)
{
struct nfc_dev *ndev = hdev->ndev;
@@ -52,7 +52,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
skb_queue_head_init(&cmd->msg_frags);
cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false;
cmd->cb = cb;
- cmd->cb_context = cb_data;
+ cmd->cb_context = cb_context;
cmd->completion_delay = completion_delay;
hci_len = payload_len + 1;
@@ -108,7 +108,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
mutex_unlock(&hdev->msg_tx_mutex);
- queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+ queue_work(system_nrt_wq, &hdev->msg_tx_work);
return 0;
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c
new file mode 100644
index 000000000000..ae1205ded87f
--- /dev/null
+++ b/net/nfc/hci/llc.c
@@ -0,0 +1,170 @@
+/*
+ * Link Layer Control manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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/nfc/llc.h>
+
+#include "llc.h"
+
+static struct list_head llc_engines;
+
+int nfc_llc_init(void)
+{
+ int r;
+
+ INIT_LIST_HEAD(&llc_engines);
+
+ r = nfc_llc_nop_register();
+ if (r)
+ goto exit;
+
+ r = nfc_llc_shdlc_register();
+ if (r)
+ goto exit;
+
+ return 0;
+
+exit:
+ nfc_llc_exit();
+ return r;
+}
+
+void nfc_llc_exit(void)
+{
+ struct nfc_llc_engine *llc_engine, *n;
+
+ list_for_each_entry_safe(llc_engine, n, &llc_engines, entry) {
+ list_del(&llc_engine->entry);
+ kfree(llc_engine->name);
+ kfree(llc_engine);
+ }
+}
+
+int nfc_llc_register(const char *name, struct nfc_llc_ops *ops)
+{
+ struct nfc_llc_engine *llc_engine;
+
+ llc_engine = kzalloc(sizeof(struct nfc_llc_engine), GFP_KERNEL);
+ if (llc_engine == NULL)
+ return -ENOMEM;
+
+ llc_engine->name = kstrdup(name, GFP_KERNEL);
+ if (llc_engine->name == NULL) {
+ kfree(llc_engine);
+ return -ENOMEM;
+ }
+ llc_engine->ops = ops;
+
+ INIT_LIST_HEAD(&llc_engine->entry);
+ list_add_tail (&llc_engine->entry, &llc_engines);
+
+ return 0;
+}
+
+static struct nfc_llc_engine *nfc_llc_name_to_engine(const char *name)
+{
+ struct nfc_llc_engine *llc_engine;
+
+ list_for_each_entry(llc_engine, &llc_engines, entry) {
+ if (strcmp(llc_engine->name, name) == 0)
+ return llc_engine;
+ }
+
+ return NULL;
+}
+
+void nfc_llc_unregister(const char *name)
+{
+ struct nfc_llc_engine *llc_engine;
+
+ llc_engine = nfc_llc_name_to_engine(name);
+ if (llc_engine == NULL)
+ return;
+
+ list_del(&llc_engine->entry);
+ kfree(llc_engine->name);
+ kfree(llc_engine);
+}
+
+struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
+ xmit_to_drv_t xmit_to_drv,
+ rcv_to_hci_t rcv_to_hci, int tx_headroom,
+ int tx_tailroom, llc_failure_t llc_failure)
+{
+ struct nfc_llc_engine *llc_engine;
+ struct nfc_llc *llc;
+
+ llc_engine = nfc_llc_name_to_engine(name);
+ if (llc_engine == NULL)
+ return NULL;
+
+ llc = kzalloc(sizeof(struct nfc_llc), GFP_KERNEL);
+ if (llc == NULL)
+ return NULL;
+
+ llc->data = llc_engine->ops->init(hdev, xmit_to_drv, rcv_to_hci,
+ tx_headroom, tx_tailroom,
+ &llc->rx_headroom, &llc->rx_tailroom,
+ llc_failure);
+ if (llc->data == NULL) {
+ kfree(llc);
+ return NULL;
+ }
+ llc->ops = llc_engine->ops;
+
+ return llc;
+}
+
+void nfc_llc_free(struct nfc_llc *llc)
+{
+ llc->ops->deinit(llc);
+ kfree(llc);
+}
+
+inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
+ int *rx_tailroom)
+{
+ *rx_headroom = llc->rx_headroom;
+ *rx_tailroom = llc->rx_tailroom;
+}
+
+inline int nfc_llc_start(struct nfc_llc *llc)
+{
+ return llc->ops->start(llc);
+}
+
+inline int nfc_llc_stop(struct nfc_llc *llc)
+{
+ return llc->ops->stop(llc);
+}
+
+inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+ llc->ops->rcv_from_drv(llc, skb);
+}
+
+inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+ return llc->ops->xmit_from_hci(llc, skb);
+}
+
+inline void *nfc_llc_get_data(struct nfc_llc *llc)
+{
+ return llc->data;
+}
diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h
new file mode 100644
index 000000000000..7be0b7f3ceb6
--- /dev/null
+++ b/net/nfc/hci/llc.h
@@ -0,0 +1,69 @@
+/*
+ * Link Layer Control manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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_LLC_H_
+#define __LOCAL_LLC_H_
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+#include <linux/skbuff.h>
+
+struct nfc_llc_ops {
+ void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+ rcv_to_hci_t rcv_to_hci, int tx_headroom,
+ int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+ llc_failure_t llc_failure);
+ void (*deinit) (struct nfc_llc *llc);
+ int (*start) (struct nfc_llc *llc);
+ int (*stop) (struct nfc_llc *llc);
+ void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
+ int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
+};
+
+struct nfc_llc_engine {
+ const char *name;
+ struct nfc_llc_ops *ops;
+ struct list_head entry;
+};
+
+struct nfc_llc {
+ void *data;
+ struct nfc_llc_ops *ops;
+ int rx_headroom;
+ int rx_tailroom;
+};
+
+void *nfc_llc_get_data(struct nfc_llc *llc);
+
+int nfc_llc_register(const char *name, struct nfc_llc_ops *ops);
+void nfc_llc_unregister(const char *name);
+
+int nfc_llc_nop_register(void);
+
+#if defined(CONFIG_NFC_SHDLC)
+int nfc_llc_shdlc_register(void);
+#else
+static inline int nfc_llc_shdlc_register(void)
+{
+ return 0;
+}
+#endif
+
+#endif /* __LOCAL_LLC_H_ */
diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c
new file mode 100644
index 000000000000..87b10291b40f
--- /dev/null
+++ b/net/nfc/hci/llc_nop.c
@@ -0,0 +1,99 @@
+/*
+ * nop (passthrough) Link Layer Control
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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/types.h>
+
+#include "llc.h"
+
+struct llc_nop {
+ struct nfc_hci_dev *hdev;
+ xmit_to_drv_t xmit_to_drv;
+ rcv_to_hci_t rcv_to_hci;
+ int tx_headroom;
+ int tx_tailroom;
+ llc_failure_t llc_failure;
+};
+
+static void *llc_nop_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+ rcv_to_hci_t rcv_to_hci, int tx_headroom,
+ int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+ llc_failure_t llc_failure)
+{
+ struct llc_nop *llc_nop;
+
+ *rx_headroom = 0;
+ *rx_tailroom = 0;
+
+ llc_nop = kzalloc(sizeof(struct llc_nop), GFP_KERNEL);
+ if (llc_nop == NULL)
+ return NULL;
+
+ llc_nop->hdev = hdev;
+ llc_nop->xmit_to_drv = xmit_to_drv;
+ llc_nop->rcv_to_hci = rcv_to_hci;
+ llc_nop->tx_headroom = tx_headroom;
+ llc_nop->tx_tailroom = tx_tailroom;
+ llc_nop->llc_failure = llc_failure;
+
+ return llc_nop;
+}
+
+static void llc_nop_deinit(struct nfc_llc *llc)
+{
+ kfree(nfc_llc_get_data(llc));
+}
+
+static int llc_nop_start(struct nfc_llc *llc)
+{
+ return 0;
+}
+
+static int llc_nop_stop(struct nfc_llc *llc)
+{
+ return 0;
+}
+
+static void llc_nop_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+ struct llc_nop *llc_nop = nfc_llc_get_data(llc);
+
+ llc_nop->rcv_to_hci(llc_nop->hdev, skb);
+}
+
+static int llc_nop_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+ struct llc_nop *llc_nop = nfc_llc_get_data(llc);
+
+ return llc_nop->xmit_to_drv(llc_nop->hdev, skb);
+}
+
+static struct nfc_llc_ops llc_nop_ops = {
+ .init = llc_nop_init,
+ .deinit = llc_nop_deinit,
+ .start = llc_nop_start,
+ .stop = llc_nop_stop,
+ .rcv_from_drv = llc_nop_rcv_from_drv,
+ .xmit_from_hci = llc_nop_xmit_from_hci,
+};
+
+int nfc_llc_nop_register(void)
+{
+ return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops);
+}
diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/llc_shdlc.c
index 6f840c18c892..8f69d791dcb3 100644
--- a/net/nfc/hci/shdlc.c
+++ b/net/nfc/hci/llc_shdlc.c
@@ -1,10 +1,11 @@
/*
+ * shdlc Link Layer Control
+ *
* Copyright (C) 2012 Intel Corporation. All rights reserved.
*
- * 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 free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,18 +20,65 @@
#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
+#include <linux/types.h>
#include <linux/sched.h>
-#include <linux/export.h>
#include <linux/wait.h>
-#include <linux/crc-ccitt.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
-#include <net/nfc/hci.h>
-#include <net/nfc/shdlc.h>
+#include "llc.h"
+
+enum shdlc_state {
+ SHDLC_DISCONNECTED = 0,
+ SHDLC_CONNECTING = 1,
+ SHDLC_NEGOTIATING = 2,
+ SHDLC_HALF_CONNECTED = 3,
+ SHDLC_CONNECTED = 4
+};
+
+struct llc_shdlc {
+ struct nfc_hci_dev *hdev;
+ xmit_to_drv_t xmit_to_drv;
+ rcv_to_hci_t rcv_to_hci;
+
+ struct mutex state_mutex;
+ enum shdlc_state state;
+ int hard_fault;
+
+ wait_queue_head_t *connect_wq;
+ int connect_tries;
+ int connect_result;
+ struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
+
+ u8 w; /* window size */
+ bool srej_support;
+
+ struct timer_list t1_timer; /* send ack timeout */
+ bool t1_active;
+
+ struct timer_list t2_timer; /* guard/retransmit timeout */
+ bool t2_active;
+
+ int ns; /* next seq num for send */
+ int nr; /* next expected seq num for receive */
+ int dnr; /* oldest sent unacked seq num */
+
+ struct sk_buff_head rcv_q;
+
+ struct sk_buff_head send_q;
+ bool rnr; /* other side is not ready to receive */
+
+ struct sk_buff_head ack_pending_q;
+
+ struct work_struct sm_work;
+
+ int tx_headroom;
+ int tx_tailroom;
+
+ llc_failure_t llc_failure;
+};
#define SHDLC_LLC_HEAD_ROOM 2
-#define SHDLC_LLC_TAIL_ROOM 2
#define SHDLC_MAX_WINDOW 4
#define SHDLC_SREJ_SUPPORT false
@@ -71,7 +119,7 @@ do { \
} while (0)
/* checks x < y <= z modulo 8 */
-static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
+static bool llc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
{
if (x < z)
return ((x < y) && (y <= z)) ? true : false;
@@ -80,7 +128,7 @@ static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
}
/* checks x <= y < z modulo 8 */
-static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
+static bool llc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
{
if (x <= z)
return ((x <= y) && (y < z)) ? true : false;
@@ -88,36 +136,21 @@ static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
return ((y >= x) || (y < z)) ? true : false;
}
-static struct sk_buff *nfc_shdlc_alloc_skb(struct nfc_shdlc *shdlc,
+static struct sk_buff *llc_shdlc_alloc_skb(struct llc_shdlc *shdlc,
int payload_len)
{
struct sk_buff *skb;
- skb = alloc_skb(shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM +
- shdlc->client_tailroom + SHDLC_LLC_TAIL_ROOM +
- payload_len, GFP_KERNEL);
+ skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM +
+ shdlc->tx_tailroom + payload_len, GFP_KERNEL);
if (skb)
- skb_reserve(skb, shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM);
+ skb_reserve(skb, shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM);
return skb;
}
-static void nfc_shdlc_add_len_crc(struct sk_buff *skb)
-{
- u16 crc;
- int len;
-
- len = skb->len + 2;
- *skb_push(skb, 1) = len;
-
- crc = crc_ccitt(0xffff, skb->data, skb->len);
- crc = ~crc;
- *skb_put(skb, 1) = crc & 0xff;
- *skb_put(skb, 1) = crc >> 8;
-}
-
/* immediately sends an S frame. */
-static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
+static int llc_shdlc_send_s_frame(struct llc_shdlc *shdlc,
enum sframe_type sframe_type, int nr)
{
int r;
@@ -125,15 +158,13 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr);
- skb = nfc_shdlc_alloc_skb(shdlc, 0);
+ skb = llc_shdlc_alloc_skb(shdlc, 0);
if (skb == NULL)
return -ENOMEM;
*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr;
- nfc_shdlc_add_len_crc(skb);
-
- r = shdlc->ops->xmit(shdlc, skb);
+ r = shdlc->xmit_to_drv(shdlc->hdev, skb);
kfree_skb(skb);
@@ -141,7 +172,7 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
}
/* immediately sends an U frame. skb may contain optional payload */
-static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
+static int llc_shdlc_send_u_frame(struct llc_shdlc *shdlc,
struct sk_buff *skb,
enum uframe_modifier uframe_modifier)
{
@@ -151,9 +182,7 @@ static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier;
- nfc_shdlc_add_len_crc(skb);
-
- r = shdlc->ops->xmit(shdlc, skb);
+ r = shdlc->xmit_to_drv(shdlc->hdev, skb);
kfree_skb(skb);
@@ -164,7 +193,7 @@ static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
* Free ack_pending frames until y_nr - 1, and reset t2 according to
* the remaining oldest ack_pending frame sent time
*/
-static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr)
+static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr)
{
struct sk_buff *skb;
int dnr = shdlc->dnr; /* MUST initially be < y_nr */
@@ -204,7 +233,7 @@ static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr)
* Receive validated frames from lower layer. skb contains HCI payload only.
* Handle according to algorithm at spec:10.8.2
*/
-static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc,
+static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc,
struct sk_buff *skb, int ns, int nr)
{
int x_ns = ns;
@@ -216,66 +245,64 @@ static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc,
goto exit;
if (x_ns != shdlc->nr) {
- nfc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
+ llc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
goto exit;
}
if (shdlc->t1_active == false) {
shdlc->t1_active = true;
- mod_timer(&shdlc->t1_timer,
+ mod_timer(&shdlc->t1_timer, jiffies +
msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w)));
pr_debug("(re)Start T1(send ack)\n");
}
if (skb->len) {
- nfc_hci_recv_frame(shdlc->hdev, skb);
+ shdlc->rcv_to_hci(shdlc->hdev, skb);
skb = NULL;
}
shdlc->nr = (shdlc->nr + 1) % 8;
- if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
- nfc_shdlc_reset_t2(shdlc, y_nr);
+ if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
+ llc_shdlc_reset_t2(shdlc, y_nr);
shdlc->dnr = y_nr;
}
exit:
- if (skb)
- kfree_skb(skb);
+ kfree_skb(skb);
}
-static void nfc_shdlc_rcv_ack(struct nfc_shdlc *shdlc, int y_nr)
+static void llc_shdlc_rcv_ack(struct llc_shdlc *shdlc, int y_nr)
{
pr_debug("remote acked up to frame %d excluded\n", y_nr);
- if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
- nfc_shdlc_reset_t2(shdlc, y_nr);
+ if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
+ llc_shdlc_reset_t2(shdlc, y_nr);
shdlc->dnr = y_nr;
}
}
-static void nfc_shdlc_requeue_ack_pending(struct nfc_shdlc *shdlc)
+static void llc_shdlc_requeue_ack_pending(struct llc_shdlc *shdlc)
{
struct sk_buff *skb;
pr_debug("ns reset to %d\n", shdlc->dnr);
while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) {
- skb_pull(skb, 2); /* remove len+control */
- skb_trim(skb, skb->len - 2); /* remove crc */
+ skb_pull(skb, 1); /* remove control field */
skb_queue_head(&shdlc->send_q, skb);
}
shdlc->ns = shdlc->dnr;
}
-static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr)
+static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr)
{
struct sk_buff *skb;
pr_debug("remote asks retransmition from frame %d\n", y_nr);
- if (nfc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
+ if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
if (shdlc->t2_active) {
del_timer_sync(&shdlc->t2_timer);
shdlc->t2_active = false;
@@ -289,12 +316,12 @@ static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr)
}
}
- nfc_shdlc_requeue_ack_pending(shdlc);
+ llc_shdlc_requeue_ack_pending(shdlc);
}
}
/* See spec RR:10.8.3 REJ:10.8.4 */
-static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
+static void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc,
enum sframe_type s_frame_type, int nr)
{
struct sk_buff *skb;
@@ -304,21 +331,21 @@ static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
switch (s_frame_type) {
case S_FRAME_RR:
- nfc_shdlc_rcv_ack(shdlc, nr);
+ llc_shdlc_rcv_ack(shdlc, nr);
if (shdlc->rnr == true) { /* see SHDLC 10.7.7 */
shdlc->rnr = false;
if (shdlc->send_q.qlen == 0) {
- skb = nfc_shdlc_alloc_skb(shdlc, 0);
+ skb = llc_shdlc_alloc_skb(shdlc, 0);
if (skb)
skb_queue_tail(&shdlc->send_q, skb);
}
}
break;
case S_FRAME_REJ:
- nfc_shdlc_rcv_rej(shdlc, nr);
+ llc_shdlc_rcv_rej(shdlc, nr);
break;
case S_FRAME_RNR:
- nfc_shdlc_rcv_ack(shdlc, nr);
+ llc_shdlc_rcv_ack(shdlc, nr);
shdlc->rnr = true;
break;
default:
@@ -326,7 +353,7 @@ static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
}
}
-static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
+static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r)
{
pr_debug("result=%d\n", r);
@@ -337,7 +364,7 @@ static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
shdlc->nr = 0;
shdlc->dnr = 0;
- shdlc->state = SHDLC_CONNECTED;
+ shdlc->state = SHDLC_HALF_CONNECTED;
} else {
shdlc->state = SHDLC_DISCONNECTED;
}
@@ -347,36 +374,36 @@ static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
wake_up(shdlc->connect_wq);
}
-static int nfc_shdlc_connect_initiate(struct nfc_shdlc *shdlc)
+static int llc_shdlc_connect_initiate(struct llc_shdlc *shdlc)
{
struct sk_buff *skb;
pr_debug("\n");
- skb = nfc_shdlc_alloc_skb(shdlc, 2);
+ skb = llc_shdlc_alloc_skb(shdlc, 2);
if (skb == NULL)
return -ENOMEM;
*skb_put(skb, 1) = SHDLC_MAX_WINDOW;
*skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0;
- return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
+ return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
}
-static int nfc_shdlc_connect_send_ua(struct nfc_shdlc *shdlc)
+static int llc_shdlc_connect_send_ua(struct llc_shdlc *shdlc)
{
struct sk_buff *skb;
pr_debug("\n");
- skb = nfc_shdlc_alloc_skb(shdlc, 0);
+ skb = llc_shdlc_alloc_skb(shdlc, 0);
if (skb == NULL)
return -ENOMEM;
- return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
+ return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
}
-static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
+static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc,
struct sk_buff *skb,
enum uframe_modifier u_frame_modifier)
{
@@ -388,8 +415,13 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
switch (u_frame_modifier) {
case U_FRAME_RSET:
- if (shdlc->state == SHDLC_NEGOCIATING) {
- /* we sent RSET, but chip wants to negociate */
+ switch (shdlc->state) {
+ case SHDLC_NEGOTIATING:
+ case SHDLC_CONNECTING:
+ /*
+ * We sent RSET, but chip wants to negociate or we
+ * got RSET before we managed to send out our.
+ */
if (skb->len > 0)
w = skb->data[0];
@@ -401,22 +433,34 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
(SHDLC_SREJ_SUPPORT || (srej_support == false))) {
shdlc->w = w;
shdlc->srej_support = srej_support;
- r = nfc_shdlc_connect_send_ua(shdlc);
- nfc_shdlc_connect_complete(shdlc, r);
+ r = llc_shdlc_connect_send_ua(shdlc);
+ llc_shdlc_connect_complete(shdlc, r);
}
- } else if (shdlc->state == SHDLC_CONNECTED) {
+ break;
+ case SHDLC_HALF_CONNECTED:
+ /*
+ * Chip resent RSET due to its timeout - Ignote it
+ * as we already sent UA.
+ */
+ break;
+ case SHDLC_CONNECTED:
/*
* Chip wants to reset link. This is unexpected and
* unsupported.
*/
shdlc->hard_fault = -ECONNRESET;
+ break;
+ default:
+ break;
}
break;
case U_FRAME_UA:
if ((shdlc->state == SHDLC_CONNECTING &&
shdlc->connect_tries > 0) ||
- (shdlc->state == SHDLC_NEGOCIATING))
- nfc_shdlc_connect_complete(shdlc, 0);
+ (shdlc->state == SHDLC_NEGOTIATING)) {
+ llc_shdlc_connect_complete(shdlc, 0);
+ shdlc->state = SHDLC_CONNECTED;
+ }
break;
default:
break;
@@ -425,7 +469,7 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
kfree_skb(skb);
}
-static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
+static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc)
{
struct sk_buff *skb;
u8 control;
@@ -443,19 +487,25 @@ static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
switch (control & SHDLC_CONTROL_HEAD_MASK) {
case SHDLC_CONTROL_HEAD_I:
case SHDLC_CONTROL_HEAD_I2:
+ if (shdlc->state == SHDLC_HALF_CONNECTED)
+ shdlc->state = SHDLC_CONNECTED;
+
ns = (control & SHDLC_CONTROL_NS_MASK) >> 3;
nr = control & SHDLC_CONTROL_NR_MASK;
- nfc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
+ llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
break;
case SHDLC_CONTROL_HEAD_S:
+ if (shdlc->state == SHDLC_HALF_CONNECTED)
+ shdlc->state = SHDLC_CONNECTED;
+
s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3;
nr = control & SHDLC_CONTROL_NR_MASK;
- nfc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
+ llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
kfree_skb(skb);
break;
case SHDLC_CONTROL_HEAD_U:
u_frame_modifier = control & SHDLC_CONTROL_M_MASK;
- nfc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
+ llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
break;
default:
pr_err("UNKNOWN Control=%d\n", control);
@@ -465,7 +515,7 @@ static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
}
}
-static int nfc_shdlc_w_used(int ns, int dnr)
+static int llc_shdlc_w_used(int ns, int dnr)
{
int unack_count;
@@ -478,7 +528,7 @@ static int nfc_shdlc_w_used(int ns, int dnr)
}
/* Send frames according to algorithm at spec:10.8.1 */
-static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
+static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc)
{
struct sk_buff *skb;
int r;
@@ -489,7 +539,7 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n",
shdlc->send_q.qlen, shdlc->ns, shdlc->dnr,
shdlc->rnr == false ? "false" : "true",
- shdlc->w - nfc_shdlc_w_used(shdlc->ns, shdlc->dnr),
+ shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr),
shdlc->ack_pending_q.qlen);
while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w &&
@@ -508,11 +558,9 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns,
shdlc->nr);
- /* SHDLC_DUMP_SKB("shdlc frame written", skb); */
-
- nfc_shdlc_add_len_crc(skb);
+ SHDLC_DUMP_SKB("shdlc frame written", skb);
- r = shdlc->ops->xmit(shdlc, skb);
+ r = shdlc->xmit_to_drv(shdlc->hdev, skb);
if (r < 0) {
shdlc->hard_fault = r;
break;
@@ -534,36 +582,36 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
}
}
-static void nfc_shdlc_connect_timeout(unsigned long data)
+static void llc_shdlc_connect_timeout(unsigned long data)
{
- struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
+ struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
pr_debug("\n");
- queue_work(shdlc->sm_wq, &shdlc->sm_work);
+ queue_work(system_nrt_wq, &shdlc->sm_work);
}
-static void nfc_shdlc_t1_timeout(unsigned long data)
+static void llc_shdlc_t1_timeout(unsigned long data)
{
- struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
+ struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
pr_debug("SoftIRQ: need to send ack\n");
- queue_work(shdlc->sm_wq, &shdlc->sm_work);
+ queue_work(system_nrt_wq, &shdlc->sm_work);
}
-static void nfc_shdlc_t2_timeout(unsigned long data)
+static void llc_shdlc_t2_timeout(unsigned long data)
{
- struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
+ struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
pr_debug("SoftIRQ: need to retransmit\n");
- queue_work(shdlc->sm_wq, &shdlc->sm_work);
+ queue_work(system_nrt_wq, &shdlc->sm_work);
}
-static void nfc_shdlc_sm_work(struct work_struct *work)
+static void llc_shdlc_sm_work(struct work_struct *work)
{
- struct nfc_shdlc *shdlc = container_of(work, struct nfc_shdlc, sm_work);
+ struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work);
int r;
pr_debug("\n");
@@ -578,46 +626,47 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
break;
case SHDLC_CONNECTING:
if (shdlc->hard_fault) {
- nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
+ llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
break;
}
if (shdlc->connect_tries++ < 5)
- r = nfc_shdlc_connect_initiate(shdlc);
+ r = llc_shdlc_connect_initiate(shdlc);
else
r = -ETIME;
if (r < 0)
- nfc_shdlc_connect_complete(shdlc, r);
+ llc_shdlc_connect_complete(shdlc, r);
else {
mod_timer(&shdlc->connect_timer, jiffies +
msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
- shdlc->state = SHDLC_NEGOCIATING;
+ shdlc->state = SHDLC_NEGOTIATING;
}
break;
- case SHDLC_NEGOCIATING:
+ case SHDLC_NEGOTIATING:
if (timer_pending(&shdlc->connect_timer) == 0) {
shdlc->state = SHDLC_CONNECTING;
- queue_work(shdlc->sm_wq, &shdlc->sm_work);
+ queue_work(system_nrt_wq, &shdlc->sm_work);
}
- nfc_shdlc_handle_rcv_queue(shdlc);
+ llc_shdlc_handle_rcv_queue(shdlc);
if (shdlc->hard_fault) {
- nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
+ llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
break;
}
break;
+ case SHDLC_HALF_CONNECTED:
case SHDLC_CONNECTED:
- nfc_shdlc_handle_rcv_queue(shdlc);
- nfc_shdlc_handle_send_queue(shdlc);
+ llc_shdlc_handle_rcv_queue(shdlc);
+ llc_shdlc_handle_send_queue(shdlc);
if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) {
pr_debug
("Handle T1(send ack) elapsed (T1 now inactive)\n");
shdlc->t1_active = false;
- r = nfc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
+ r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
shdlc->nr);
if (r < 0)
shdlc->hard_fault = r;
@@ -629,12 +678,12 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
shdlc->t2_active = false;
- nfc_shdlc_requeue_ack_pending(shdlc);
- nfc_shdlc_handle_send_queue(shdlc);
+ llc_shdlc_requeue_ack_pending(shdlc);
+ llc_shdlc_handle_send_queue(shdlc);
}
if (shdlc->hard_fault) {
- nfc_hci_driver_failure(shdlc->hdev, shdlc->hard_fault);
+ shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
}
break;
default:
@@ -647,7 +696,7 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
* Called from syscall context to establish shdlc link. Sleeps until
* link is ready or failure.
*/
-static int nfc_shdlc_connect(struct nfc_shdlc *shdlc)
+static int llc_shdlc_connect(struct llc_shdlc *shdlc)
{
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq);
@@ -662,14 +711,14 @@ static int nfc_shdlc_connect(struct nfc_shdlc *shdlc)
mutex_unlock(&shdlc->state_mutex);
- queue_work(shdlc->sm_wq, &shdlc->sm_work);
+ queue_work(system_nrt_wq, &shdlc->sm_work);
wait_event(connect_wq, shdlc->connect_result != 1);
return shdlc->connect_result;
}
-static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
+static void llc_shdlc_disconnect(struct llc_shdlc *shdlc)
{
pr_debug("\n");
@@ -679,7 +728,7 @@ static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
mutex_unlock(&shdlc->state_mutex);
- queue_work(shdlc->sm_wq, &shdlc->sm_work);
+ queue_work(system_nrt_wq, &shdlc->sm_work);
}
/*
@@ -687,7 +736,7 @@ static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
* skb contains only LLC header and payload.
* If skb == NULL, it is a notification that the link below is dead.
*/
-void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb)
+static void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb)
{
if (skb == NULL) {
pr_err("NULL Frame -> link is dead\n");
@@ -697,176 +746,37 @@ void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb)
skb_queue_tail(&shdlc->rcv_q, skb);
}
- queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-EXPORT_SYMBOL(nfc_shdlc_recv_frame);
-
-static int nfc_shdlc_open(struct nfc_hci_dev *hdev)
-{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
- int r;
-
- pr_debug("\n");
-
- if (shdlc->ops->open) {
- r = shdlc->ops->open(shdlc);
- if (r < 0)
- return r;
- }
-
- r = nfc_shdlc_connect(shdlc);
- if (r < 0 && shdlc->ops->close)
- shdlc->ops->close(shdlc);
-
- return r;
-}
-
-static void nfc_shdlc_close(struct nfc_hci_dev *hdev)
-{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
- pr_debug("\n");
-
- nfc_shdlc_disconnect(shdlc);
-
- if (shdlc->ops->close)
- shdlc->ops->close(shdlc);
+ queue_work(system_nrt_wq, &shdlc->sm_work);
}
-static int nfc_shdlc_hci_ready(struct nfc_hci_dev *hdev)
+static void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+ rcv_to_hci_t rcv_to_hci, int tx_headroom,
+ int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+ llc_failure_t llc_failure)
{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
- int r = 0;
-
- pr_debug("\n");
+ struct llc_shdlc *shdlc;
- if (shdlc->ops->hci_ready)
- r = shdlc->ops->hci_ready(shdlc);
-
- return r;
-}
-
-static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
-{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
- SHDLC_DUMP_SKB("queuing HCP packet to shdlc", skb);
-
- skb_queue_tail(&shdlc->send_q, skb);
+ *rx_headroom = SHDLC_LLC_HEAD_ROOM;
+ *rx_tailroom = 0;
- queue_work(shdlc->sm_wq, &shdlc->sm_work);
-
- return 0;
-}
-
-static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev,
- u32 im_protocols, u32 tm_protocols)
-{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
- pr_debug("\n");
-
- if (shdlc->ops->start_poll)
- return shdlc->ops->start_poll(shdlc,
- im_protocols, tm_protocols);
-
- return 0;
-}
-
-static int nfc_shdlc_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
- struct nfc_target *target)
-{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
- if (shdlc->ops->target_from_gate)
- return shdlc->ops->target_from_gate(shdlc, gate, target);
-
- return -EPERM;
-}
-
-static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev,
- u8 gate,
- struct nfc_target *target)
-{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
- pr_debug("\n");
-
- if (shdlc->ops->complete_target_discovered)
- return shdlc->ops->complete_target_discovered(shdlc, gate,
- target);
-
- return 0;
-}
-
-static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev,
- struct nfc_target *target,
- struct sk_buff *skb,
- struct sk_buff **res_skb)
-{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
- if (shdlc->ops->data_exchange)
- return shdlc->ops->data_exchange(shdlc, target, skb, res_skb);
-
- return -EPERM;
-}
-
-static int nfc_shdlc_check_presence(struct nfc_hci_dev *hdev,
- struct nfc_target *target)
-{
- struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
- if (shdlc->ops->check_presence)
- return shdlc->ops->check_presence(shdlc, target);
-
- return 0;
-}
-
-static struct nfc_hci_ops shdlc_ops = {
- .open = nfc_shdlc_open,
- .close = nfc_shdlc_close,
- .hci_ready = nfc_shdlc_hci_ready,
- .xmit = nfc_shdlc_xmit,
- .start_poll = nfc_shdlc_start_poll,
- .target_from_gate = nfc_shdlc_target_from_gate,
- .complete_target_discovered = nfc_shdlc_complete_target_discovered,
- .data_exchange = nfc_shdlc_data_exchange,
- .check_presence = nfc_shdlc_check_presence,
-};
-
-struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
- struct nfc_hci_init_data *init_data,
- u32 protocols,
- int tx_headroom, int tx_tailroom,
- int max_link_payload, const char *devname)
-{
- struct nfc_shdlc *shdlc;
- int r;
- char name[32];
-
- if (ops->xmit == NULL)
- return NULL;
-
- shdlc = kzalloc(sizeof(struct nfc_shdlc), GFP_KERNEL);
+ shdlc = kzalloc(sizeof(struct llc_shdlc), GFP_KERNEL);
if (shdlc == NULL)
return NULL;
mutex_init(&shdlc->state_mutex);
- shdlc->ops = ops;
shdlc->state = SHDLC_DISCONNECTED;
init_timer(&shdlc->connect_timer);
shdlc->connect_timer.data = (unsigned long)shdlc;
- shdlc->connect_timer.function = nfc_shdlc_connect_timeout;
+ shdlc->connect_timer.function = llc_shdlc_connect_timeout;
init_timer(&shdlc->t1_timer);
shdlc->t1_timer.data = (unsigned long)shdlc;
- shdlc->t1_timer.function = nfc_shdlc_t1_timeout;
+ shdlc->t1_timer.function = llc_shdlc_t1_timeout;
init_timer(&shdlc->t2_timer);
shdlc->t2_timer.data = (unsigned long)shdlc;
- shdlc->t2_timer.function = nfc_shdlc_t2_timeout;
+ shdlc->t2_timer.function = llc_shdlc_t2_timeout;
shdlc->w = SHDLC_MAX_WINDOW;
shdlc->srej_support = SHDLC_SREJ_SUPPORT;
@@ -875,77 +785,73 @@ struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
skb_queue_head_init(&shdlc->send_q);
skb_queue_head_init(&shdlc->ack_pending_q);
- INIT_WORK(&shdlc->sm_work, nfc_shdlc_sm_work);
- snprintf(name, sizeof(name), "%s_shdlc_sm_wq", devname);
- shdlc->sm_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
- WQ_MEM_RECLAIM, 1);
- if (shdlc->sm_wq == NULL)
- goto err_allocwq;
+ INIT_WORK(&shdlc->sm_work, llc_shdlc_sm_work);
- shdlc->client_headroom = tx_headroom;
- shdlc->client_tailroom = tx_tailroom;
-
- shdlc->hdev = nfc_hci_allocate_device(&shdlc_ops, init_data, protocols,
- tx_headroom + SHDLC_LLC_HEAD_ROOM,
- tx_tailroom + SHDLC_LLC_TAIL_ROOM,
- max_link_payload);
- if (shdlc->hdev == NULL)
- goto err_allocdev;
-
- nfc_hci_set_clientdata(shdlc->hdev, shdlc);
-
- r = nfc_hci_register_device(shdlc->hdev);
- if (r < 0)
- goto err_regdev;
+ shdlc->hdev = hdev;
+ shdlc->xmit_to_drv = xmit_to_drv;
+ shdlc->rcv_to_hci = rcv_to_hci;
+ shdlc->tx_headroom = tx_headroom;
+ shdlc->tx_tailroom = tx_tailroom;
+ shdlc->llc_failure = llc_failure;
return shdlc;
+}
-err_regdev:
- nfc_hci_free_device(shdlc->hdev);
+static void llc_shdlc_deinit(struct nfc_llc *llc)
+{
+ struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
-err_allocdev:
- destroy_workqueue(shdlc->sm_wq);
+ skb_queue_purge(&shdlc->rcv_q);
+ skb_queue_purge(&shdlc->send_q);
+ skb_queue_purge(&shdlc->ack_pending_q);
-err_allocwq:
kfree(shdlc);
-
- return NULL;
}
-EXPORT_SYMBOL(nfc_shdlc_allocate);
-void nfc_shdlc_free(struct nfc_shdlc *shdlc)
+static int llc_shdlc_start(struct nfc_llc *llc)
{
- pr_debug("\n");
+ struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
- nfc_hci_unregister_device(shdlc->hdev);
- nfc_hci_free_device(shdlc->hdev);
+ return llc_shdlc_connect(shdlc);
+}
- destroy_workqueue(shdlc->sm_wq);
+static int llc_shdlc_stop(struct nfc_llc *llc)
+{
+ struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
- skb_queue_purge(&shdlc->rcv_q);
- skb_queue_purge(&shdlc->send_q);
- skb_queue_purge(&shdlc->ack_pending_q);
+ llc_shdlc_disconnect(shdlc);
- kfree(shdlc);
+ return 0;
}
-EXPORT_SYMBOL(nfc_shdlc_free);
-void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata)
+static void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
{
- pr_debug("\n");
+ struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
- shdlc->clientdata = clientdata;
+ llc_shdlc_recv_frame(shdlc, skb);
}
-EXPORT_SYMBOL(nfc_shdlc_set_clientdata);
-void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc)
+static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
{
- return shdlc->clientdata;
+ struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+ skb_queue_tail(&shdlc->send_q, skb);
+
+ queue_work(system_nrt_wq, &shdlc->sm_work);
+
+ return 0;
}
-EXPORT_SYMBOL(nfc_shdlc_get_clientdata);
-struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc)
+static struct nfc_llc_ops llc_shdlc_ops = {
+ .init = llc_shdlc_init,
+ .deinit = llc_shdlc_deinit,
+ .start = llc_shdlc_start,
+ .stop = llc_shdlc_stop,
+ .rcv_from_drv = llc_shdlc_rcv_from_drv,
+ .xmit_from_hci = llc_shdlc_xmit_from_hci,
+};
+
+int nfc_llc_shdlc_register(void)
{
- return shdlc->hdev;
+ return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops);
}
-EXPORT_SYMBOL(nfc_shdlc_get_hci_dev);
diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c
index b982b5b890d7..c45ccd6c094c 100644
--- a/net/nfc/llcp/commands.c
+++ b/net/nfc/llcp/commands.c
@@ -312,6 +312,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
+ nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
+
return nfc_data_exchange(dev, local->target_idx, skb,
nfc_llcp_recv, local);
}
diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c
index 82f0f7588b46..c12c5ef3d036 100644
--- a/net/nfc/llcp/llcp.c
+++ b/net/nfc/llcp/llcp.c
@@ -56,7 +56,7 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
sk_for_each_safe(sk, node, tmp, &local->sockets.head) {
llcp_sock = nfc_llcp_sock(sk);
- lock_sock(sk);
+ bh_lock_sock(sk);
if (sk->sk_state == LLCP_CONNECTED)
nfc_put_device(llcp_sock->dev);
@@ -68,26 +68,26 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue,
accept_queue) {
accept_sk = &lsk->sk;
- lock_sock(accept_sk);
+ bh_lock_sock(accept_sk);
nfc_llcp_accept_unlink(accept_sk);
accept_sk->sk_state = LLCP_CLOSED;
- release_sock(accept_sk);
+ bh_unlock_sock(accept_sk);
sock_orphan(accept_sk);
}
if (listen == true) {
- release_sock(sk);
+ bh_unlock_sock(sk);
continue;
}
}
sk->sk_state = LLCP_CLOSED;
- release_sock(sk);
+ bh_unlock_sock(sk);
sock_orphan(sk);
@@ -114,9 +114,9 @@ static void local_release(struct kref *ref)
nfc_llcp_socket_release(local, false);
del_timer_sync(&local->link_timer);
skb_queue_purge(&local->tx_queue);
- destroy_workqueue(local->tx_wq);
- destroy_workqueue(local->rx_wq);
- destroy_workqueue(local->timeout_wq);
+ cancel_work_sync(&local->tx_work);
+ cancel_work_sync(&local->rx_work);
+ cancel_work_sync(&local->timeout_work);
kfree_skb(local->rx_pending);
kfree(local);
}
@@ -181,7 +181,7 @@ static void nfc_llcp_symm_timer(unsigned long data)
pr_err("SYMM timeout\n");
- queue_work(local->timeout_wq, &local->timeout_work);
+ queue_work(system_nrt_wq, &local->timeout_work);
}
struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
@@ -426,6 +426,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
u8 *miux_tlv, miux_length;
__be16 miux;
u8 gb_len = 0;
+ int ret = 0;
version = LLCP_VERSION_11;
version_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &version,
@@ -450,8 +451,8 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
gb_len += ARRAY_SIZE(llcp_magic);
if (gb_len > NFC_MAX_GT_LEN) {
- kfree(version_tlv);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
gb_cur = local->gb;
@@ -471,12 +472,15 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
memcpy(gb_cur, miux_tlv, miux_length);
gb_cur += miux_length;
+ local->gb_len = gb_len;
+
+out:
kfree(version_tlv);
kfree(lto_tlv);
+ kfree(wks_tlv);
+ kfree(miux_tlv);
- local->gb_len = gb_len;
-
- return 0;
+ return ret;
}
u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
@@ -554,6 +558,46 @@ static void nfc_llcp_set_nrns(struct nfc_llcp_sock *sock, struct sk_buff *pdu)
sock->recv_ack_n = (sock->recv_n - 1) % 16;
}
+void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
+ struct sk_buff *skb, u8 direction)
+{
+ struct hlist_node *node;
+ struct sk_buff *skb_copy = NULL, *nskb;
+ struct sock *sk;
+ u8 *data;
+
+ read_lock(&local->raw_sockets.lock);
+
+ sk_for_each(sk, node, &local->raw_sockets.head) {
+ if (sk->sk_state != LLCP_BOUND)
+ continue;
+
+ if (skb_copy == NULL) {
+ skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE,
+ GFP_ATOMIC);
+
+ if (skb_copy == NULL)
+ continue;
+
+ data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE);
+
+ data[0] = local->dev ? local->dev->idx : 0xFF;
+ data[1] = direction;
+ }
+
+ nskb = skb_clone(skb_copy, GFP_ATOMIC);
+ if (!nskb)
+ continue;
+
+ if (sock_queue_rcv_skb(sk, nskb))
+ kfree_skb(nskb);
+ }
+
+ read_unlock(&local->raw_sockets.lock);
+
+ kfree_skb(skb_copy);
+}
+
static void nfc_llcp_tx_work(struct work_struct *work)
{
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
@@ -574,6 +618,9 @@ static void nfc_llcp_tx_work(struct work_struct *work)
DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb->len, true);
+ nfc_llcp_send_to_raw_sock(local, skb,
+ NFC_LLCP_DIRECTION_TX);
+
ret = nfc_data_exchange(local->dev, local->target_idx,
skb, nfc_llcp_recv, local);
@@ -1018,6 +1065,8 @@ static void nfc_llcp_rx_work(struct work_struct *work)
print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET,
16, 1, skb->data, skb->len, true);
+ nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
+
switch (ptype) {
case LLCP_PDU_SYMM:
pr_debug("SYMM\n");
@@ -1052,7 +1101,7 @@ static void nfc_llcp_rx_work(struct work_struct *work)
}
- queue_work(local->tx_wq, &local->tx_work);
+ queue_work(system_nrt_wq, &local->tx_work);
kfree_skb(local->rx_pending);
local->rx_pending = NULL;
@@ -1071,7 +1120,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
local->rx_pending = skb_get(skb);
del_timer(&local->link_timer);
- queue_work(local->rx_wq, &local->rx_work);
+ queue_work(system_nrt_wq, &local->rx_work);
return;
}
@@ -1086,7 +1135,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
local->rx_pending = skb_get(skb);
del_timer(&local->link_timer);
- queue_work(local->rx_wq, &local->rx_work);
+ queue_work(system_nrt_wq, &local->rx_work);
return 0;
}
@@ -1121,7 +1170,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
if (rf_mode == NFC_RF_INITIATOR) {
pr_debug("Queueing Tx work\n");
- queue_work(local->tx_wq, &local->tx_work);
+ queue_work(system_nrt_wq, &local->tx_work);
} else {
mod_timer(&local->link_timer,
jiffies + msecs_to_jiffies(local->remote_lto));
@@ -1130,10 +1179,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
int nfc_llcp_register_device(struct nfc_dev *ndev)
{
- struct device *dev = &ndev->dev;
struct nfc_llcp_local *local;
- char name[32];
- int err;
local = kzalloc(sizeof(struct nfc_llcp_local), GFP_KERNEL);
if (local == NULL)
@@ -1149,41 +1195,15 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
skb_queue_head_init(&local->tx_queue);
INIT_WORK(&local->tx_work, nfc_llcp_tx_work);
- snprintf(name, sizeof(name), "%s_llcp_tx_wq", dev_name(dev));
- local->tx_wq =
- alloc_workqueue(name,
- WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
- 1);
- if (local->tx_wq == NULL) {
- err = -ENOMEM;
- goto err_local;
- }
local->rx_pending = NULL;
INIT_WORK(&local->rx_work, nfc_llcp_rx_work);
- snprintf(name, sizeof(name), "%s_llcp_rx_wq", dev_name(dev));
- local->rx_wq =
- alloc_workqueue(name,
- WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
- 1);
- if (local->rx_wq == NULL) {
- err = -ENOMEM;
- goto err_tx_wq;
- }
INIT_WORK(&local->timeout_work, nfc_llcp_timeout_work);
- snprintf(name, sizeof(name), "%s_llcp_timeout_wq", dev_name(dev));
- local->timeout_wq =
- alloc_workqueue(name,
- WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
- 1);
- if (local->timeout_wq == NULL) {
- err = -ENOMEM;
- goto err_rx_wq;
- }
- local->sockets.lock = __RW_LOCK_UNLOCKED(local->sockets.lock);
- local->connecting_sockets.lock = __RW_LOCK_UNLOCKED(local->connecting_sockets.lock);
+ rwlock_init(&local->sockets.lock);
+ rwlock_init(&local->connecting_sockets.lock);
+ rwlock_init(&local->raw_sockets.lock);
nfc_llcp_build_gb(local);
@@ -1193,17 +1213,6 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
list_add(&llcp_devices, &local->list);
return 0;
-
-err_rx_wq:
- destroy_workqueue(local->rx_wq);
-
-err_tx_wq:
- destroy_workqueue(local->tx_wq);
-
-err_local:
- kfree(local);
-
- return 0;
}
void nfc_llcp_unregister_device(struct nfc_dev *dev)
diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h
index 83b8bba5a280..fdb2d24e60bd 100644
--- a/net/nfc/llcp/llcp.h
+++ b/net/nfc/llcp/llcp.h
@@ -56,12 +56,9 @@ struct nfc_llcp_local {
struct timer_list link_timer;
struct sk_buff_head tx_queue;
- struct workqueue_struct *tx_wq;
struct work_struct tx_work;
- struct workqueue_struct *rx_wq;
struct work_struct rx_work;
struct sk_buff *rx_pending;
- struct workqueue_struct *timeout_wq;
struct work_struct timeout_work;
u32 target_idx;
@@ -89,6 +86,7 @@ struct nfc_llcp_local {
/* sockets array */
struct llcp_sock_list sockets;
struct llcp_sock_list connecting_sockets;
+ struct llcp_sock_list raw_sockets;
};
struct nfc_llcp_sock {
@@ -187,6 +185,8 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local);
void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap);
int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock);
+void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
+ struct sk_buff *skb, u8 direction);
/* Sock API */
struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp);
diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c
index ddeb9aa398f0..40f056debf9a 100644
--- a/net/nfc/llcp/sock.c
+++ b/net/nfc/llcp/sock.c
@@ -142,6 +142,60 @@ error:
return ret;
}
+static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr,
+ int alen)
+{
+ struct sock *sk = sock->sk;
+ struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+ struct nfc_llcp_local *local;
+ struct nfc_dev *dev;
+ struct sockaddr_nfc_llcp llcp_addr;
+ int len, ret = 0;
+
+ if (!addr || addr->sa_family != AF_NFC)
+ return -EINVAL;
+
+ pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family);
+
+ memset(&llcp_addr, 0, sizeof(llcp_addr));
+ len = min_t(unsigned int, sizeof(llcp_addr), alen);
+ memcpy(&llcp_addr, addr, len);
+
+ lock_sock(sk);
+
+ if (sk->sk_state != LLCP_CLOSED) {
+ ret = -EBADFD;
+ goto error;
+ }
+
+ dev = nfc_get_device(llcp_addr.dev_idx);
+ if (dev == NULL) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ local = nfc_llcp_find_local(dev);
+ if (local == NULL) {
+ ret = -ENODEV;
+ goto put_dev;
+ }
+
+ llcp_sock->dev = dev;
+ llcp_sock->local = nfc_llcp_local_get(local);
+ llcp_sock->nfc_protocol = llcp_addr.nfc_protocol;
+
+ nfc_llcp_sock_link(&local->raw_sockets, sk);
+
+ sk->sk_state = LLCP_BOUND;
+
+put_dev:
+ nfc_put_device(dev);
+
+error:
+ release_sock(sk);
+ return ret;
+}
+
static int llcp_sock_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
@@ -300,9 +354,6 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *uaddr,
pr_debug("%p %d %d %d\n", sk, llcp_sock->target_idx,
llcp_sock->dsap, llcp_sock->ssap);
- if (llcp_sock == NULL || llcp_sock->dev == NULL)
- return -EBADFD;
-
uaddr->sa_family = AF_NFC;
*len = sizeof(struct sockaddr_nfc_llcp);
@@ -421,7 +472,10 @@ static int llcp_sock_release(struct socket *sock)
release_sock(sk);
- nfc_llcp_sock_unlink(&local->sockets, sk);
+ if (sock->type == SOCK_RAW)
+ nfc_llcp_sock_unlink(&local->raw_sockets, sk);
+ else
+ nfc_llcp_sock_unlink(&local->sockets, sk);
out:
sock_orphan(sk);
@@ -617,7 +671,7 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (!(flags & MSG_PEEK)) {
/* SOCK_STREAM: re-queue skb if it contains unreceived data */
- if (sk->sk_type == SOCK_STREAM) {
+ if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_RAW) {
skb_pull(skb, copied);
if (skb->len) {
skb_queue_head(&sk->sk_receive_queue, skb);
@@ -658,6 +712,26 @@ static const struct proto_ops llcp_sock_ops = {
.mmap = sock_no_mmap,
};
+static const struct proto_ops llcp_rawsock_ops = {
+ .family = PF_NFC,
+ .owner = THIS_MODULE,
+ .bind = llcp_raw_sock_bind,
+ .connect = sock_no_connect,
+ .release = llcp_sock_release,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = llcp_sock_getname,
+ .poll = llcp_sock_poll,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = llcp_sock_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
static void llcp_sock_destruct(struct sock *sk)
{
struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
@@ -735,10 +809,15 @@ static int llcp_sock_create(struct net *net, struct socket *sock,
pr_debug("%p\n", sock);
- if (sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM)
+ if (sock->type != SOCK_STREAM &&
+ sock->type != SOCK_DGRAM &&
+ sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
- sock->ops = &llcp_sock_ops;
+ if (sock->type == SOCK_RAW)
+ sock->ops = &llcp_rawsock_ops;
+ else
+ sock->ops = &llcp_sock_ops;
sk = nfc_llcp_sock_alloc(sock, sock->type, GFP_ATOMIC);
if (sk == NULL)
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index f81efe13985a..acf9abb7d99b 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -176,6 +176,27 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt)
(1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
}
+struct nci_set_config_param {
+ __u8 id;
+ size_t len;
+ __u8 *val;
+};
+
+static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt)
+{
+ struct nci_set_config_param *param = (struct nci_set_config_param *)opt;
+ struct nci_core_set_config_cmd cmd;
+
+ BUG_ON(param->len > NCI_MAX_PARAM_LEN);
+
+ cmd.num_params = 1;
+ cmd.param.id = param->id;
+ cmd.param.len = param->len;
+ memcpy(cmd.param.val, param->val, param->len);
+
+ nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
+}
+
static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
{
struct nci_rf_disc_cmd cmd;
@@ -388,6 +409,32 @@ static int nci_dev_down(struct nfc_dev *nfc_dev)
return nci_close_device(ndev);
}
+static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ struct nci_set_config_param param;
+ __u8 local_gb[NFC_MAX_GT_LEN];
+ int i, rc = 0;
+
+ param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
+ if ((param.val == NULL) || (param.len == 0))
+ return rc;
+
+ if (param.len > NCI_MAX_PARAM_LEN)
+ return -EINVAL;
+
+ for (i = 0; i < param.len; i++)
+ local_gb[param.len-1-i] = param.val[i];
+
+ param.id = NCI_PN_ATR_REQ_GEN_BYTES;
+ param.val = local_gb;
+
+ rc = nci_request(ndev, nci_set_config_req, (unsigned long)&param,
+ msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+
+ return rc;
+}
+
static int nci_start_poll(struct nfc_dev *nfc_dev,
__u32 im_protocols, __u32 tm_protocols)
{
@@ -415,6 +462,14 @@ static int nci_start_poll(struct nfc_dev *nfc_dev,
return -EBUSY;
}
+ if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ rc = nci_set_local_general_bytes(nfc_dev);
+ if (rc) {
+ pr_err("failed to set local general bytes\n");
+ return rc;
+ }
+ }
+
rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
@@ -509,7 +564,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
- pr_debug("target_idx %d\n", target->idx);
+ pr_debug("entry\n");
if (!ndev->target_active_prot) {
pr_err("unable to deactivate target, no active target\n");
@@ -524,6 +579,38 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
}
}
+
+static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
+ __u8 comm_mode, __u8 *gb, size_t gb_len)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
+ pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode);
+
+ rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP);
+ if (rc)
+ return rc;
+
+ rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb,
+ ndev->remote_gb_len);
+ if (!rc)
+ rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE,
+ NFC_RF_INITIATOR);
+
+ return rc;
+}
+
+static int nci_dep_link_down(struct nfc_dev *nfc_dev)
+{
+ pr_debug("entry\n");
+
+ nci_deactivate_target(nfc_dev, NULL);
+
+ return 0;
+}
+
+
static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
struct sk_buff *skb,
data_exchange_cb_t cb, void *cb_context)
@@ -557,6 +644,8 @@ static struct nfc_ops nci_nfc_ops = {
.dev_down = nci_dev_down,
.start_poll = nci_start_poll,
.stop_poll = nci_stop_poll,
+ .dep_link_up = nci_dep_link_up,
+ .dep_link_down = nci_dep_link_down,
.activate_target = nci_activate_target,
.deactivate_target = nci_deactivate_target,
.im_transceive = nci_transceive,
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index af7a93b04393..b2aa98ef0927 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -176,6 +176,8 @@ static int nci_add_new_protocol(struct nci_dev *ndev,
protocol = NFC_PROTO_ISO14443_B_MASK;
else if (rf_protocol == NCI_RF_PROTOCOL_T3T)
protocol = NFC_PROTO_FELICA_MASK;
+ else if (rf_protocol == NCI_RF_PROTOCOL_NFC_DEP)
+ protocol = NFC_PROTO_NFC_DEP_MASK;
else
protocol = 0;
@@ -361,6 +363,33 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
return NCI_STATUS_OK;
}
+static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
+ struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
+{
+ struct activation_params_poll_nfc_dep *poll;
+ int i;
+
+ switch (ntf->activation_rf_tech_and_mode) {
+ case NCI_NFC_A_PASSIVE_POLL_MODE:
+ case NCI_NFC_F_PASSIVE_POLL_MODE:
+ poll = &ntf->activation_params.poll_nfc_dep;
+ poll->atr_res_len = min_t(__u8, *data++, 63);
+ pr_debug("atr_res_len %d\n", poll->atr_res_len);
+ if (poll->atr_res_len > 0) {
+ for (i = 0; i < poll->atr_res_len; i++)
+ poll->atr_res[poll->atr_res_len-1-i] = data[i];
+ }
+ break;
+
+ default:
+ pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
+ ntf->activation_rf_tech_and_mode);
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
+ }
+
+ return NCI_STATUS_OK;
+}
+
static void nci_target_auto_activated(struct nci_dev *ndev,
struct nci_rf_intf_activated_ntf *ntf)
{
@@ -454,6 +483,11 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
&ntf, data);
break;
+ case NCI_RF_INTERFACE_NFC_DEP:
+ err = nci_extract_activation_params_nfc_dep(ndev,
+ &ntf, data);
+ break;
+
case NCI_RF_INTERFACE_FRAME:
/* no activation params */
break;
@@ -473,6 +507,24 @@ exit:
/* set the available credits to initial value */
atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
+
+ /* store general bytes to be reported later in dep_link_up */
+ if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
+ ndev->remote_gb_len = 0;
+
+ if (ntf.activation_params_len > 0) {
+ /* ATR_RES general bytes at offset 15 */
+ ndev->remote_gb_len = min_t(__u8,
+ (ntf.activation_params
+ .poll_nfc_dep.atr_res_len
+ - NFC_ATR_RES_GT_OFFSET),
+ NFC_MAX_GT_LEN);
+ memcpy(ndev->remote_gb,
+ (ntf.activation_params.poll_nfc_dep
+ .atr_res + NFC_ATR_RES_GT_OFFSET),
+ ndev->remote_gb_len);
+ }
+ }
}
if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 3003c3390e49..dd072f38ad00 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -119,6 +119,16 @@ exit:
nci_req_complete(ndev, rsp_1->status);
}
+static void nci_core_set_config_rsp_packet(struct nci_dev *ndev,
+ struct sk_buff *skb)
+{
+ struct nci_core_set_config_rsp *rsp = (void *) skb->data;
+
+ pr_debug("status 0x%x\n", rsp->status);
+
+ nci_req_complete(ndev, rsp->status);
+}
+
static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev,
struct sk_buff *skb)
{
@@ -194,6 +204,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
nci_core_init_rsp_packet(ndev, skb);
break;
+ case NCI_OP_CORE_SET_CONFIG_RSP:
+ nci_core_set_config_rsp_packet(ndev, skb);
+ break;
+
case NCI_OP_RF_DISCOVER_MAP_RSP:
nci_rf_disc_map_rsp_packet(ndev, skb);
break;
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 4bbb70e32d1e..c1b5285cbde7 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -761,31 +761,63 @@ static struct genl_ops nfc_genl_ops[] = {
},
};
-static int nfc_genl_rcv_nl_event(struct notifier_block *this,
- unsigned long event, void *ptr)
+
+struct urelease_work {
+ struct work_struct w;
+ int portid;
+};
+
+static void nfc_urelease_event_work(struct work_struct *work)
{
- struct netlink_notify *n = ptr;
+ struct urelease_work *w = container_of(work, struct urelease_work, w);
struct class_dev_iter iter;
struct nfc_dev *dev;
- if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
- goto out;
+ pr_debug("portid %d\n", w->portid);
- pr_debug("NETLINK_URELEASE event from id %d\n", n->portid);
+ mutex_lock(&nfc_devlist_mutex);
nfc_device_iter_init(&iter);
dev = nfc_device_iter_next(&iter);
while (dev) {
- if (dev->genl_data.poll_req_portid == n->portid) {
+ mutex_lock(&dev->genl_data.genl_data_mutex);
+
+ if (dev->genl_data.poll_req_portid == w->portid) {
nfc_stop_poll(dev);
dev->genl_data.poll_req_portid = 0;
}
+
+ mutex_unlock(&dev->genl_data.genl_data_mutex);
+
dev = nfc_device_iter_next(&iter);
}
nfc_device_iter_exit(&iter);
+ mutex_unlock(&nfc_devlist_mutex);
+
+ kfree(w);
+}
+
+static int nfc_genl_rcv_nl_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct netlink_notify *n = ptr;
+ struct urelease_work *w;
+
+ if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
+ goto out;
+
+ pr_debug("NETLINK_URELEASE event from id %d\n", n->portid);
+
+ w = kmalloc(sizeof(*w), GFP_ATOMIC);
+ if (w) {
+ INIT_WORK((struct work_struct *) w, nfc_urelease_event_work);
+ w->portid = n->portid;
+ schedule_work((struct work_struct *) w);
+ }
+
out:
return NOTIFY_DONE;
}
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index c275bad12068..a5c952741279 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -270,6 +270,7 @@ static bool __rfkill_set_hw_state(struct rfkill *rfkill,
static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
{
unsigned long flags;
+ bool prev, curr;
int err;
if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
@@ -284,6 +285,8 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
rfkill->ops->query(rfkill, rfkill->data);
spin_lock_irqsave(&rfkill->lock, flags);
+ prev = rfkill->state & RFKILL_BLOCK_SW;
+
if (rfkill->state & RFKILL_BLOCK_SW)
rfkill->state |= RFKILL_BLOCK_SW_PREV;
else
@@ -313,10 +316,13 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
}
rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
+ curr = rfkill->state & RFKILL_BLOCK_SW;
spin_unlock_irqrestore(&rfkill->lock, flags);
rfkill_led_trigger_event(rfkill);
- rfkill_event(rfkill);
+
+ if (prev != curr)
+ rfkill_event(rfkill);
}
#ifdef CONFIG_RFKILL_INPUT
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index ec7fcee5bad6..8016fee0752b 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -612,6 +612,17 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
}
EXPORT_SYMBOL(cfg80211_del_sta);
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+ enum nl80211_connect_failed_reason reason,
+ gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ nl80211_send_conn_failed_event(rdev, dev, mac_addr, reason, gfp);
+}
+EXPORT_SYMBOL(cfg80211_conn_failed);
+
struct cfg80211_mgmt_registration {
struct list_head list;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 139946dc8020..0418a6d5c1a6 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -8364,6 +8364,40 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *mac_addr,
+ enum nl80211_connect_failed_reason reason,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONN_FAILED);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+ nla_put_u32(msg, NL80211_ATTR_CONN_FAILED_REASON, reason))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ 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);
+}
+
static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
const u8 *addr, gfp_t gfp)
{
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 9f2616fffb40..f6153516068c 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -91,6 +91,11 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *mac_addr,
gfp_t gfp);
+void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *mac_addr,
+ enum nl80211_connect_failed_reason reason,
+ gfp_t gfp);
+
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u32 nlpid,
int freq, int sig_dbm,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 4de18ae18d34..3b8cbbc214db 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -510,9 +510,11 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
*
* This lets us know if a specific frequency rule is or is not relevant to
* a specific frequency's band. Bands are device specific and artificial
- * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is
- * safe for now to assume that a frequency rule should not be part of a
- * frequency's band if the start freq or end freq are off by more than 2 GHz.
+ * definitions (the "2.4 GHz band", the "5 GHz band" and the "60GHz band"),
+ * however it is safe for now to assume that a frequency rule should not be
+ * part of a frequency's band if the start freq or end freq are off by more
+ * than 2 GHz for the 2.4 and 5 GHz bands, and by more than 10 GHz for the
+ * 60 GHz band.
* This resolution can be lowered and should be considered as we add
* regulatory rule support for other "bands".
**/
@@ -520,9 +522,16 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
u32 freq_khz)
{
#define ONE_GHZ_IN_KHZ 1000000
- if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+ /*
+ * From 802.11ad: directional multi-gigabit (DMG):
+ * Pertaining to operation in a frequency band containing a channel
+ * with the Channel starting frequency above 45 GHz.
+ */
+ u32 limit = freq_khz > 45 * ONE_GHZ_IN_KHZ ?
+ 10 * ONE_GHZ_IN_KHZ : 2 * ONE_GHZ_IN_KHZ;
+ if (abs(freq_khz - freq_range->start_freq_khz) <= limit)
return true;
- if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+ if (abs(freq_khz - freq_range->end_freq_khz) <= limit)
return true;
return false;
#undef ONE_GHZ_IN_KHZ
@@ -2199,7 +2208,6 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
static int __set_regdom(const struct ieee80211_regdomain *rd)
{
const struct ieee80211_regdomain *intersected_rd = NULL;
- struct cfg80211_registered_device *rdev = NULL;
struct wiphy *request_wiphy;
/* Some basic sanity checks first */
@@ -2311,24 +2319,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return 0;
}
- if (!intersected_rd)
- return -EINVAL;
-
- rdev = wiphy_to_dev(request_wiphy);
-
- rdev->country_ie_alpha2[0] = rd->alpha2[0];
- rdev->country_ie_alpha2[1] = rd->alpha2[1];
- rdev->env = last_request->country_ie_env;
-
- BUG_ON(intersected_rd == rd);
-
- kfree(rd);
- rd = NULL;
-
- reset_regdomains(false);
- cfg80211_regdomain = intersected_rd;
-
- return 0;
+ return -EINVAL;
}