summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2012-11-28 19:56:03 +0400
committerJohn W. Linville <linville@tuxdriver.com>2012-11-28 19:56:03 +0400
commit79d38f7d6cf545ff838dd5227869f3916d1d100d (patch)
tree859c8071aab68fd32f36ffb9ebc04cf12db6e5e0 /net
parent03f52a0a554210d5049eeed9f1bb29047dc807cb (diff)
parent0751f8654602e4255f0b9c17784d8100d5896010 (diff)
downloadlinux-79d38f7d6cf545ff838dd5227869f3916d1d100d.tar.xz
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Conflicts: drivers/net/wireless/iwlwifi/pcie/tx.c
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/Kconfig1
-rw-r--r--net/bluetooth/a2mp.c4
-rw-r--r--net/bluetooth/amp.c97
-rw-r--r--net/bluetooth/bnep/netdev.c1
-rw-r--r--net/bluetooth/cmtp/capi.c2
-rw-r--r--net/bluetooth/cmtp/sock.c2
-rw-r--r--net/bluetooth/hci_conn.c6
-rw-r--r--net/bluetooth/hci_core.c163
-rw-r--r--net/bluetooth/hci_event.c351
-rw-r--r--net/bluetooth/l2cap_core.c1010
-rw-r--r--net/bluetooth/l2cap_sock.c5
-rw-r--r--net/bluetooth/mgmt.c100
-rw-r--r--net/mac80211/aes_cmac.c1
-rw-r--r--net/mac80211/agg-rx.c2
-rw-r--r--net/mac80211/agg-tx.c12
-rw-r--r--net/mac80211/cfg.c258
-rw-r--r--net/mac80211/chan.c130
-rw-r--r--net/mac80211/debugfs_key.c6
-rw-r--r--net/mac80211/debugfs_netdev.c68
-rw-r--r--net/mac80211/debugfs_sta.c19
-rw-r--r--net/mac80211/driver-ops.h75
-rw-r--r--net/mac80211/ht.c4
-rw-r--r--net/mac80211/ibss.c75
-rw-r--r--net/mac80211/ieee80211_i.h50
-rw-r--r--net/mac80211/iface.c60
-rw-r--r--net/mac80211/key.c15
-rw-r--r--net/mac80211/key.h8
-rw-r--r--net/mac80211/main.c50
-rw-r--r--net/mac80211/mesh.c36
-rw-r--r--net/mac80211/mesh.h14
-rw-r--r--net/mac80211/mesh_plink.c47
-rw-r--r--net/mac80211/mesh_sync.c46
-rw-r--r--net/mac80211/mlme.c198
-rw-r--r--net/mac80211/offchannel.c13
-rw-r--r--net/mac80211/pm.c48
-rw-r--r--net/mac80211/rate.c5
-rw-r--r--net/mac80211/rate.h2
-rw-r--r--net/mac80211/rx.c169
-rw-r--r--net/mac80211/scan.c9
-rw-r--r--net/mac80211/sta_info.c12
-rw-r--r--net/mac80211/sta_info.h27
-rw-r--r--net/mac80211/status.c145
-rw-r--r--net/mac80211/trace.h116
-rw-r--r--net/mac80211/tx.c21
-rw-r--r--net/mac80211/util.c194
-rw-r--r--net/mac80211/wme.c40
-rw-r--r--net/nfc/hci/command.c4
-rw-r--r--net/nfc/hci/core.c25
-rw-r--r--net/nfc/llcp/commands.c32
-rw-r--r--net/nfc/llcp/llcp.c17
-rw-r--r--net/wireless/Kconfig5
-rw-r--r--net/wireless/ap.c1
-rw-r--r--net/wireless/chan.c280
-rw-r--r--net/wireless/core.c7
-rw-r--r--net/wireless/core.h30
-rw-r--r--net/wireless/ibss.c27
-rw-r--r--net/wireless/mesh.c49
-rw-r--r--net/wireless/mlme.c36
-rw-r--r--net/wireless/nl80211.c590
-rw-r--r--net/wireless/nl80211.h8
-rw-r--r--net/wireless/rdev-ops.h53
-rw-r--r--net/wireless/scan.c45
-rw-r--r--net/wireless/trace.h338
-rw-r--r--net/wireless/util.c174
-rw-r--r--net/wireless/wext-compat.c32
-rw-r--r--net/wireless/wext-sme.c11
66 files changed, 3960 insertions, 1521 deletions
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 1c11d0dcd863..d3f3f7b1d32c 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -48,4 +48,3 @@ source "net/bluetooth/cmtp/Kconfig"
source "net/bluetooth/hidp/Kconfig"
source "drivers/bluetooth/Kconfig"
-
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index d5136cfb57e2..2f67d5ecc907 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -423,7 +423,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id);
- mgr->bredr_chan->ctrl_id = rsp->id;
+ mgr->bredr_chan->remote_amp_id = rsp->id;
amp_create_phylink(hdev, mgr, hcon);
@@ -939,7 +939,7 @@ void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status)
goto clean;
req->local_id = hdev->id;
- req->remote_id = bredr_chan->ctrl_id;
+ req->remote_id = bredr_chan->remote_amp_id;
memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len);
a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req);
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index 231d7ef53ecb..1b0d92c0643a 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -372,3 +372,100 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
}
+
+void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
+{
+ struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
+ struct amp_mgr *mgr = hs_hcon->amp_mgr;
+ struct l2cap_chan *bredr_chan;
+
+ BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr);
+
+ if (!bredr_hdev || !mgr || !mgr->bredr_chan)
+ return;
+
+ bredr_chan = mgr->bredr_chan;
+
+ l2cap_chan_lock(bredr_chan);
+
+ set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags);
+ bredr_chan->remote_amp_id = hs_hcon->remote_id;
+ bredr_chan->local_amp_id = hs_hcon->hdev->id;
+ bredr_chan->hs_hcon = hs_hcon;
+ bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu;
+
+ __l2cap_physical_cfm(bredr_chan, 0);
+
+ l2cap_chan_unlock(bredr_chan);
+
+ hci_dev_put(bredr_hdev);
+}
+
+void amp_create_logical_link(struct l2cap_chan *chan)
+{
+ struct hci_cp_create_accept_logical_link cp;
+ struct hci_conn *hcon;
+ struct hci_dev *hdev;
+
+ BT_DBG("chan %p", chan);
+
+ if (!chan->hs_hcon)
+ return;
+
+ hdev = hci_dev_hold(chan->hs_hcon->hdev);
+ if (!hdev)
+ return;
+
+ BT_DBG("chan %p dst %pMR", chan, chan->conn->dst);
+
+ hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst);
+ if (!hcon)
+ goto done;
+
+ cp.phy_handle = hcon->handle;
+
+ cp.tx_flow_spec.id = chan->local_id;
+ cp.tx_flow_spec.stype = chan->local_stype;
+ cp.tx_flow_spec.msdu = cpu_to_le16(chan->local_msdu);
+ cp.tx_flow_spec.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
+ cp.tx_flow_spec.acc_lat = cpu_to_le32(chan->local_acc_lat);
+ cp.tx_flow_spec.flush_to = cpu_to_le32(chan->local_flush_to);
+
+ cp.rx_flow_spec.id = chan->remote_id;
+ cp.rx_flow_spec.stype = chan->remote_stype;
+ cp.rx_flow_spec.msdu = cpu_to_le16(chan->remote_msdu);
+ cp.rx_flow_spec.sdu_itime = cpu_to_le32(chan->remote_sdu_itime);
+ cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat);
+ cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to);
+
+ if (hcon->out)
+ hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
+ &cp);
+ else
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
+ &cp);
+
+done:
+ hci_dev_put(hdev);
+}
+
+void amp_disconnect_logical_link(struct hci_chan *hchan)
+{
+ struct hci_conn *hcon = hchan->conn;
+ struct hci_cp_disconn_logical_link cp;
+
+ if (hcon->state != BT_CONNECTED) {
+ BT_DBG("hchan %p not connected", hchan);
+ return;
+ }
+
+ cp.log_handle = cpu_to_le16(hchan->handle);
+ hci_send_cmd(hcon->hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp);
+}
+
+void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason)
+{
+ BT_DBG("hchan %p", hchan);
+
+ hci_chan_del(hchan);
+}
diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
index 98f86f91d47c..e58c8b32589c 100644
--- a/net/bluetooth/bnep/netdev.c
+++ b/net/bluetooth/bnep/netdev.c
@@ -25,7 +25,6 @@
SOFTWARE IS DISCLAIMED.
*/
-#include <linux/export.h>
#include <linux/etherdevice.h>
#include <net/bluetooth/bluetooth.h>
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 50f0d135eb8f..a4a9d4b6816c 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -20,7 +20,7 @@
SOFTWARE IS DISCLAIMED.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/types.h>
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index aacb802d1ee4..1c57482112b6 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -20,7 +20,7 @@
SOFTWARE IS DISCLAIMED.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/types.h>
#include <linux/capability.h>
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index fe646211c61f..25bfce0666eb 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -502,6 +502,9 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
{
struct hci_conn *le;
+ if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags))
+ return ERR_PTR(-ENOTSUPP);
+
le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
if (!le) {
le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
@@ -959,6 +962,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
chan->conn = conn;
skb_queue_head_init(&chan->data_q);
+ chan->state = BT_CONNECTED;
list_add_rcu(&chan->list, &conn->chan_list);
@@ -976,6 +980,8 @@ void hci_chan_del(struct hci_chan *chan)
synchronize_rcu();
+ hci_conn_put(conn);
+
skb_queue_purge(&chan->data_q);
kfree(chan);
}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index f01e5e135b99..7140f83328a2 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -178,48 +178,13 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
static void bredr_init(struct hci_dev *hdev)
{
- struct hci_cp_delete_stored_link_key cp;
- __le16 param;
- __u8 flt_type;
-
hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED;
- /* Mandatory initialization */
-
/* Read Local Supported Features */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
/* Read Local Version */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
-
- /* Read Buffer Size (ACL mtu, max pkt, etc.) */
- hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
-
- /* Read BD Address */
- hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
-
- /* Read Class of Device */
- hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
-
- /* Read Local Name */
- hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);
-
- /* Read Voice Setting */
- hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
-
- /* Optional initialization */
-
- /* Clear Event Filters */
- flt_type = HCI_FLT_CLEAR_ALL;
- hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
-
- /* Connection accept timeout ~20 secs */
- param = __constant_cpu_to_le16(0x7d00);
- hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
-
- bacpy(&cp.bdaddr, BDADDR_ANY);
- cp.delete_all = 1;
- hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
}
static void amp_init(struct hci_dev *hdev)
@@ -273,14 +238,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
}
}
-static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
-{
- BT_DBG("%s", hdev->name);
-
- /* Read LE buffer size */
- hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
-}
-
static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
{
__u8 scan = opt;
@@ -477,6 +434,8 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
BT_DBG("cache %p, %pMR", cache, &data->bdaddr);
+ hci_remove_remote_oob_data(hdev, &data->bdaddr);
+
if (ssp)
*ssp = data->ssp_mode;
@@ -637,6 +596,99 @@ done:
return err;
}
+static u8 create_ad(struct hci_dev *hdev, u8 *ptr)
+{
+ u8 ad_len = 0, flags = 0;
+ size_t name_len;
+
+ if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
+ flags |= LE_AD_GENERAL;
+
+ if (!lmp_bredr_capable(hdev))
+ flags |= LE_AD_NO_BREDR;
+
+ if (lmp_le_br_capable(hdev))
+ flags |= LE_AD_SIM_LE_BREDR_CTRL;
+
+ if (lmp_host_le_br_capable(hdev))
+ flags |= LE_AD_SIM_LE_BREDR_HOST;
+
+ if (flags) {
+ BT_DBG("adv flags 0x%02x", flags);
+
+ ptr[0] = 2;
+ ptr[1] = EIR_FLAGS;
+ ptr[2] = flags;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
+ ptr[0] = 2;
+ ptr[1] = EIR_TX_POWER;
+ ptr[2] = (u8) hdev->adv_tx_power;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ name_len = strlen(hdev->dev_name);
+ if (name_len > 0) {
+ size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+ if (name_len > max_len) {
+ name_len = max_len;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ ad_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ return ad_len;
+}
+
+int hci_update_ad(struct hci_dev *hdev)
+{
+ struct hci_cp_le_set_adv_data cp;
+ u8 len;
+ int err;
+
+ hci_dev_lock(hdev);
+
+ if (!lmp_le_capable(hdev)) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+
+ len = create_ad(hdev, cp.data);
+
+ if (hdev->adv_data_len == len &&
+ memcmp(cp.data, hdev->adv_data, len) == 0) {
+ err = 0;
+ goto unlock;
+ }
+
+ memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+ hdev->adv_data_len = len;
+
+ cp.length = len;
+ err = hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+
+unlock:
+ hci_dev_unlock(hdev);
+
+ return err;
+}
+
/* ---- HCI ioctl helpers ---- */
int hci_dev_open(__u16 dev)
@@ -687,10 +739,6 @@ int hci_dev_open(__u16 dev)
ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
- if (lmp_host_le_capable(hdev))
- ret = __hci_request(hdev, hci_le_init_req, 0,
- HCI_INIT_TIMEOUT);
-
clear_bit(HCI_INIT, &hdev->flags);
}
@@ -698,6 +746,7 @@ int hci_dev_open(__u16 dev)
hci_dev_hold(hdev);
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
+ hci_update_ad(hdev);
if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
mgmt_valid_hdev(hdev)) {
hci_dev_lock(hdev);
@@ -1039,10 +1088,17 @@ int hci_get_dev_info(void __user *arg)
di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4);
di.flags = hdev->flags;
di.pkt_type = hdev->pkt_type;
- di.acl_mtu = hdev->acl_mtu;
- di.acl_pkts = hdev->acl_pkts;
- di.sco_mtu = hdev->sco_mtu;
- di.sco_pkts = hdev->sco_pkts;
+ if (lmp_bredr_capable(hdev)) {
+ di.acl_mtu = hdev->acl_mtu;
+ di.acl_pkts = hdev->acl_pkts;
+ di.sco_mtu = hdev->sco_mtu;
+ di.sco_pkts = hdev->sco_pkts;
+ } else {
+ di.acl_mtu = hdev->le_mtu;
+ di.acl_pkts = hdev->le_pkts;
+ di.sco_mtu = 0;
+ di.sco_pkts = 0;
+ }
di.link_policy = hdev->link_policy;
di.link_mode = hdev->link_mode;
@@ -1617,6 +1673,9 @@ int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
BT_DBG("%s", hdev->name);
+ if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
+ return -ENOTSUPP;
+
if (work_busy(&hdev->le_scan))
return -EINPROGRESS;
@@ -1643,6 +1702,8 @@ struct hci_dev *hci_alloc_dev(void)
hdev->esco_type = (ESCO_HV1);
hdev->link_mode = (HCI_LM_ACCEPT);
hdev->io_capability = 0x03; /* No Input No Output */
+ hdev->inq_tx_power = HCI_TX_POWER_INVALID;
+ hdev->adv_tx_power = HCI_TX_POWER_INVALID;
hdev->sniff_max_interval = 800;
hdev->sniff_min_interval = 80;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 0383635f91fb..9f5c5f244502 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -24,7 +24,6 @@
/* Bluetooth HCI event handling. */
-#include <linux/export.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -203,6 +202,11 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
BIT(HCI_PERIODIC_INQ));
hdev->discovery.state = DISCOVERY_STOPPED;
+ hdev->inq_tx_power = HCI_TX_POWER_INVALID;
+ hdev->adv_tx_power = HCI_TX_POWER_INVALID;
+
+ memset(hdev->adv_data, 0, sizeof(hdev->adv_data));
+ hdev->adv_data_len = 0;
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -225,6 +229,9 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_unlock(hdev);
+ if (!status && !test_bit(HCI_INIT, &hdev->flags))
+ hci_update_ad(hdev);
+
hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status);
}
@@ -440,7 +447,7 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
- void *sent;
+ struct hci_cp_write_ssp_mode *sent;
BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -448,10 +455,17 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
if (!sent)
return;
+ if (!status) {
+ if (sent->mode)
+ hdev->host_features[0] |= LMP_HOST_SSP;
+ else
+ hdev->host_features[0] &= ~LMP_HOST_SSP;
+ }
+
if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status);
+ mgmt_ssp_enable_complete(hdev, sent->mode, status);
else if (!status) {
- if (*((u8 *) sent))
+ if (sent->mode)
set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
else
clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
@@ -460,10 +474,10 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
{
- if (hdev->features[6] & LMP_EXT_INQ)
+ if (lmp_ext_inq_capable(hdev))
return 2;
- if (hdev->features[3] & LMP_RSSI_INQ)
+ if (lmp_inq_rssi_capable(hdev))
return 1;
if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 &&
@@ -507,28 +521,30 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return;
- events[4] |= 0x01; /* Flow Specification Complete */
- events[4] |= 0x02; /* Inquiry Result with RSSI */
- events[4] |= 0x04; /* Read Remote Extended Features Complete */
- events[5] |= 0x08; /* Synchronous Connection Complete */
- events[5] |= 0x10; /* Synchronous Connection Changed */
+ if (lmp_bredr_capable(hdev)) {
+ events[4] |= 0x01; /* Flow Specification Complete */
+ events[4] |= 0x02; /* Inquiry Result with RSSI */
+ events[4] |= 0x04; /* Read Remote Extended Features Complete */
+ events[5] |= 0x08; /* Synchronous Connection Complete */
+ events[5] |= 0x10; /* Synchronous Connection Changed */
+ }
- if (hdev->features[3] & LMP_RSSI_INQ)
+ if (lmp_inq_rssi_capable(hdev))
events[4] |= 0x02; /* Inquiry Result with RSSI */
if (lmp_sniffsubr_capable(hdev))
events[5] |= 0x20; /* Sniff Subrating */
- if (hdev->features[5] & LMP_PAUSE_ENC)
+ if (lmp_pause_enc_capable(hdev))
events[5] |= 0x80; /* Encryption Key Refresh Complete */
- if (hdev->features[6] & LMP_EXT_INQ)
+ if (lmp_ext_inq_capable(hdev))
events[5] |= 0x40; /* Extended Inquiry Result */
if (lmp_no_flush_capable(hdev))
events[7] |= 0x01; /* Enhanced Flush Complete */
- if (hdev->features[7] & LMP_LSTO)
+ if (lmp_lsto_capable(hdev))
events[6] |= 0x80; /* Link Supervision Timeout Changed */
if (lmp_ssp_capable(hdev)) {
@@ -548,6 +564,53 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
events[7] |= 0x20; /* LE Meta-Event */
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
+
+ if (lmp_le_capable(hdev)) {
+ memset(events, 0, sizeof(events));
+ events[0] = 0x1f;
+ hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK,
+ sizeof(events), events);
+ }
+}
+
+static void bredr_setup(struct hci_dev *hdev)
+{
+ struct hci_cp_delete_stored_link_key cp;
+ __le16 param;
+ __u8 flt_type;
+
+ /* Read Buffer Size (ACL mtu, max pkt, etc.) */
+ hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
+
+ /* Read Class of Device */
+ hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
+
+ /* Read Local Name */
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);
+
+ /* Read Voice Setting */
+ hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
+
+ /* Clear Event Filters */
+ flt_type = HCI_FLT_CLEAR_ALL;
+ hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
+
+ /* Connection accept timeout ~20 secs */
+ param = __constant_cpu_to_le16(0x7d00);
+ hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
+
+ bacpy(&cp.bdaddr, BDADDR_ANY);
+ cp.delete_all = 1;
+ hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
+}
+
+static void le_setup(struct hci_dev *hdev)
+{
+ /* Read LE Buffer Size */
+ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
+
+ /* Read LE Advertising Channel TX Power */
+ hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
}
static void hci_setup(struct hci_dev *hdev)
@@ -555,6 +618,15 @@ static void hci_setup(struct hci_dev *hdev)
if (hdev->dev_type != HCI_BREDR)
return;
+ /* Read BD Address */
+ hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
+
+ if (lmp_bredr_capable(hdev))
+ bredr_setup(hdev);
+
+ if (lmp_le_capable(hdev))
+ le_setup(hdev);
+
hci_setup_event_mask(hdev);
if (hdev->hci_ver > BLUETOOTH_VER_1_1)
@@ -575,13 +647,13 @@ static void hci_setup(struct hci_dev *hdev)
}
}
- if (hdev->features[3] & LMP_RSSI_INQ)
+ if (lmp_inq_rssi_capable(hdev))
hci_setup_inquiry_mode(hdev);
- if (hdev->features[7] & LMP_INQ_TX_PWR)
+ if (lmp_inq_tx_pwr_capable(hdev))
hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
- if (hdev->features[7] & LMP_EXTFEATURES) {
+ if (lmp_ext_feat_capable(hdev)) {
struct hci_cp_read_local_ext_features cp;
cp.page = 0x01;
@@ -628,11 +700,11 @@ static void hci_setup_link_policy(struct hci_dev *hdev)
if (lmp_rswitch_capable(hdev))
link_policy |= HCI_LP_RSWITCH;
- if (hdev->features[0] & LMP_HOLD)
+ if (lmp_hold_capable(hdev))
link_policy |= HCI_LP_HOLD;
if (lmp_sniff_capable(hdev))
link_policy |= HCI_LP_SNIFF;
- if (hdev->features[1] & LMP_PARK)
+ if (lmp_park_capable(hdev))
link_policy |= HCI_LP_PARK;
cp.policy = cpu_to_le16(link_policy);
@@ -722,10 +794,10 @@ static void hci_set_le_support(struct hci_dev *hdev)
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
cp.le = 1;
- cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+ cp.simul = !!lmp_le_br_capable(hdev);
}
- if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE))
+ if (cp.le != !!lmp_host_le_capable(hdev))
hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp),
&cp);
}
@@ -1018,6 +1090,31 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
}
+static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_adv_tx_power *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+ if (!rp->status) {
+ hdev->adv_tx_power = rp->tx_power;
+ if (!test_bit(HCI_INIT, &hdev->flags))
+ hci_update_ad(hdev);
+ }
+
+ hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status);
+}
+
+static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ hci_req_complete(hdev, HCI_OP_LE_SET_EVENT_MASK, status);
+}
+
static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
@@ -1093,6 +1190,33 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
+static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 *sent, status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE);
+ if (!sent)
+ return;
+
+ hci_dev_lock(hdev);
+
+ if (!status) {
+ if (*sent)
+ set_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+ else
+ clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+ }
+
+ hci_dev_unlock(hdev);
+
+ if (!test_bit(HCI_INIT, &hdev->flags))
+ hci_update_ad(hdev);
+
+ hci_req_complete(hdev, HCI_OP_LE_SET_ADV_ENABLE, status);
+}
+
static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -1207,6 +1331,11 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
hdev->host_features[0] |= LMP_HOST_LE;
else
hdev->host_features[0] &= ~LMP_HOST_LE;
+
+ if (sent->simul)
+ hdev->host_features[0] |= LMP_HOST_LE_BREDR;
+ else
+ hdev->host_features[0] &= ~LMP_HOST_LE_BREDR;
}
if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
@@ -1718,14 +1847,23 @@ static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status)
BT_DBG("%s status 0x%2.2x", hdev->name, status);
- if (status)
- return;
-
cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK);
if (!cp)
return;
- amp_write_remote_assoc(hdev, cp->phy_handle);
+ hci_dev_lock(hdev);
+
+ if (status) {
+ struct hci_conn *hcon;
+
+ hcon = hci_conn_hash_lookup_handle(hdev, cp->phy_handle);
+ if (hcon)
+ hci_conn_del(hcon);
+ } else {
+ amp_write_remote_assoc(hdev, cp->phy_handle);
+ }
+
+ hci_dev_unlock(hdev);
}
static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
@@ -1744,6 +1882,11 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
amp_write_remote_assoc(hdev, cp->phy_handle);
}
+static void hci_cs_create_logical_link(struct hci_dev *hdev, u8 status)
+{
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+}
+
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -2441,6 +2584,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_read_buffer_size(hdev, skb);
break;
+ case HCI_OP_LE_READ_ADV_TX_POWER:
+ hci_cc_le_read_adv_tx_power(hdev, skb);
+ break;
+
+ case HCI_OP_LE_SET_EVENT_MASK:
+ hci_cc_le_set_event_mask(hdev, skb);
+ break;
+
case HCI_OP_USER_CONFIRM_REPLY:
hci_cc_user_confirm_reply(hdev, skb);
break;
@@ -2461,6 +2612,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_set_scan_param(hdev, skb);
break;
+ case HCI_OP_LE_SET_ADV_ENABLE:
+ hci_cc_le_set_adv_enable(hdev, skb);
+ break;
+
case HCI_OP_LE_SET_SCAN_ENABLE:
hci_cc_le_set_scan_enable(hdev, skb);
break;
@@ -2570,6 +2725,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_accept_phylink(hdev, ev->status);
break;
+ case HCI_OP_CREATE_LOGICAL_LINK:
+ hci_cs_create_logical_link(hdev, ev->status);
+ break;
+
default:
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
break;
@@ -3544,6 +3703,130 @@ unlock:
hci_dev_unlock(hdev);
}
+static void hci_phy_link_complete_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_phy_link_complete *ev = (void *) skb->data;
+ struct hci_conn *hcon, *bredr_hcon;
+
+ BT_DBG("%s handle 0x%2.2x status 0x%2.2x", hdev->name, ev->phy_handle,
+ ev->status);
+
+ hci_dev_lock(hdev);
+
+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
+ if (!hcon) {
+ hci_dev_unlock(hdev);
+ return;
+ }
+
+ if (ev->status) {
+ hci_conn_del(hcon);
+ hci_dev_unlock(hdev);
+ return;
+ }
+
+ bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon;
+
+ hcon->state = BT_CONNECTED;
+ bacpy(&hcon->dst, &bredr_hcon->dst);
+
+ hci_conn_hold(hcon);
+ hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
+ hci_conn_put(hcon);
+
+ hci_conn_hold_device(hcon);
+ hci_conn_add_sysfs(hcon);
+
+ amp_physical_cfm(bredr_hcon, hcon);
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_loglink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_logical_link_complete *ev = (void *) skb->data;
+ struct hci_conn *hcon;
+ struct hci_chan *hchan;
+ struct amp_mgr *mgr;
+
+ BT_DBG("%s log_handle 0x%4.4x phy_handle 0x%2.2x status 0x%2.2x",
+ hdev->name, le16_to_cpu(ev->handle), ev->phy_handle,
+ ev->status);
+
+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
+ if (!hcon)
+ return;
+
+ /* Create AMP hchan */
+ hchan = hci_chan_create(hcon);
+ if (!hchan)
+ return;
+
+ hchan->handle = le16_to_cpu(ev->handle);
+
+ BT_DBG("hcon %p mgr %p hchan %p", hcon, hcon->amp_mgr, hchan);
+
+ mgr = hcon->amp_mgr;
+ if (mgr && mgr->bredr_chan) {
+ struct l2cap_chan *bredr_chan = mgr->bredr_chan;
+
+ l2cap_chan_lock(bredr_chan);
+
+ bredr_chan->conn->mtu = hdev->block_mtu;
+ l2cap_logical_cfm(bredr_chan, hchan, 0);
+ hci_conn_hold(hcon);
+
+ l2cap_chan_unlock(bredr_chan);
+ }
+}
+
+static void hci_disconn_loglink_complete_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_disconn_logical_link_complete *ev = (void *) skb->data;
+ struct hci_chan *hchan;
+
+ BT_DBG("%s log handle 0x%4.4x status 0x%2.2x", hdev->name,
+ le16_to_cpu(ev->handle), ev->status);
+
+ if (ev->status)
+ return;
+
+ hci_dev_lock(hdev);
+
+ hchan = hci_chan_lookup_handle(hdev, le16_to_cpu(ev->handle));
+ if (!hchan)
+ goto unlock;
+
+ amp_destroy_logical_link(hchan, ev->reason);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_disconn_phy_link_complete *ev = (void *) skb->data;
+ struct hci_conn *hcon;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
+
+ if (ev->status)
+ return;
+
+ hci_dev_lock(hdev);
+
+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
+ if (hcon) {
+ hcon->state = BT_CLOSED;
+ hci_conn_del(hcon);
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_le_conn_complete *ev = (void *) skb->data;
@@ -3871,6 +4154,22 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_remote_oob_data_request_evt(hdev, skb);
break;
+ case HCI_EV_PHY_LINK_COMPLETE:
+ hci_phy_link_complete_evt(hdev, skb);
+ break;
+
+ case HCI_EV_LOGICAL_LINK_COMPLETE:
+ hci_loglink_complete_evt(hdev, skb);
+ break;
+
+ case HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE:
+ hci_disconn_loglink_complete_evt(hdev, skb);
+ break;
+
+ case HCI_EV_DISCONN_PHY_LINK_COMPLETE:
+ hci_disconn_phylink_complete_evt(hdev, skb);
+ break;
+
case HCI_EV_NUM_COMP_BLOCKS:
hci_num_comp_blocks_evt(hdev, skb);
break;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 08efc256c931..b52f66d22437 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -38,6 +38,7 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
#include <net/bluetooth/a2mp.h>
+#include <net/bluetooth/amp.h>
bool disable_ertm;
@@ -100,6 +101,23 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
return c;
}
+/* Find channel with given DCID.
+ * Returns locked channel.
+ */
+static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
+ u16 cid)
+{
+ struct l2cap_chan *c;
+
+ mutex_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_dcid(conn, cid);
+ if (c)
+ l2cap_chan_lock(c);
+ mutex_unlock(&conn->chan_lock);
+
+ return c;
+}
+
static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
u8 ident)
{
@@ -112,6 +130,20 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
return NULL;
}
+static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
+ u8 ident)
+{
+ struct l2cap_chan *c;
+
+ mutex_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_ident(conn, ident);
+ if (c)
+ l2cap_chan_lock(c);
+ mutex_unlock(&conn->chan_lock);
+
+ return c;
+}
+
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
{
struct l2cap_chan *c;
@@ -546,6 +578,13 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
mgr->bredr_chan = NULL;
}
+ if (chan->hs_hchan) {
+ struct hci_chan *hs_hchan = chan->hs_hchan;
+
+ BT_DBG("chan %p disconnect hs_hchan %p", chan, hs_hchan);
+ amp_disconnect_logical_link(hs_hchan);
+ }
+
chan->ops->teardown(chan, err);
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
@@ -718,6 +757,12 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
hci_send_acl(conn->hchan, skb, flags);
}
+static bool __chan_is_moving(struct l2cap_chan *chan)
+{
+ return chan->move_state != L2CAP_MOVE_STABLE &&
+ chan->move_state != L2CAP_MOVE_WAIT_PREPARE;
+}
+
static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
{
struct hci_conn *hcon = chan->conn->hcon;
@@ -726,6 +771,15 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
skb->priority);
+ if (chan->hs_hcon && !__chan_is_moving(chan)) {
+ if (chan->hs_hchan)
+ hci_send_acl(chan->hs_hchan, skb, ACL_COMPLETE);
+ else
+ kfree_skb(skb);
+
+ return;
+ }
+
if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
lmp_no_flush_capable(hcon->hdev))
flags = ACL_START_NO_FLUSH;
@@ -901,6 +955,9 @@ static void l2cap_send_sframe(struct l2cap_chan *chan,
if (!control->sframe)
return;
+ if (__chan_is_moving(chan))
+ return;
+
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) &&
!control->poll)
control->final = 1;
@@ -964,6 +1021,12 @@ static bool __amp_capable(struct l2cap_chan *chan)
return false;
}
+static bool l2cap_check_efs(struct l2cap_chan *chan)
+{
+ /* Check EFS parameters */
+ return true;
+}
+
void l2cap_send_conn_req(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
@@ -979,6 +1042,76 @@ void l2cap_send_conn_req(struct l2cap_chan *chan)
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
}
+static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id)
+{
+ struct l2cap_create_chan_req req;
+ req.scid = cpu_to_le16(chan->scid);
+ req.psm = chan->psm;
+ req.amp_id = amp_id;
+
+ chan->ident = l2cap_get_ident(chan->conn);
+
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ,
+ sizeof(req), &req);
+}
+
+static void l2cap_move_setup(struct l2cap_chan *chan)
+{
+ struct sk_buff *skb;
+
+ BT_DBG("chan %p", chan);
+
+ if (chan->mode != L2CAP_MODE_ERTM)
+ return;
+
+ __clear_retrans_timer(chan);
+ __clear_monitor_timer(chan);
+ __clear_ack_timer(chan);
+
+ chan->retry_count = 0;
+ skb_queue_walk(&chan->tx_q, skb) {
+ if (bt_cb(skb)->control.retries)
+ bt_cb(skb)->control.retries = 1;
+ else
+ break;
+ }
+
+ chan->expected_tx_seq = chan->buffer_seq;
+
+ clear_bit(CONN_REJ_ACT, &chan->conn_state);
+ clear_bit(CONN_SREJ_ACT, &chan->conn_state);
+ l2cap_seq_list_clear(&chan->retrans_list);
+ l2cap_seq_list_clear(&chan->srej_list);
+ skb_queue_purge(&chan->srej_q);
+
+ chan->tx_state = L2CAP_TX_STATE_XMIT;
+ chan->rx_state = L2CAP_RX_STATE_MOVE;
+
+ set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+}
+
+static void l2cap_move_done(struct l2cap_chan *chan)
+{
+ u8 move_role = chan->move_role;
+ BT_DBG("chan %p", chan);
+
+ chan->move_state = L2CAP_MOVE_STABLE;
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+
+ if (chan->mode != L2CAP_MODE_ERTM)
+ return;
+
+ switch (move_role) {
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL);
+ chan->rx_state = L2CAP_RX_STATE_WAIT_F;
+ break;
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ chan->rx_state = L2CAP_RX_STATE_WAIT_P;
+ break;
+ }
+}
+
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
/* This clears all conf flags, including CONF_NOT_COMPLETE */
@@ -1695,6 +1828,9 @@ static void l2cap_streaming_send(struct l2cap_chan *chan,
BT_DBG("chan %p, skbs %p", chan, skbs);
+ if (__chan_is_moving(chan))
+ return;
+
skb_queue_splice_tail_init(skbs, &chan->tx_q);
while (!skb_queue_empty(&chan->tx_q)) {
@@ -1737,6 +1873,9 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return 0;
+ if (__chan_is_moving(chan))
+ return 0;
+
while (chan->tx_send_head &&
chan->unacked_frames < chan->remote_tx_win &&
chan->tx_state == L2CAP_TX_STATE_XMIT) {
@@ -1802,6 +1941,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return;
+ if (__chan_is_moving(chan))
+ return;
+
while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) {
seq = l2cap_seq_list_pop(&chan->retrans_list);
@@ -2144,7 +2286,9 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
/* PDU size is derived from the HCI MTU */
pdu_len = chan->conn->mtu;
- pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
+ /* Constrain PDU size for BR/EDR connections */
+ if (!chan->hs_hcon)
+ pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
/* Adjust for largest possible L2CAP overhead. */
if (chan->fcs)
@@ -2788,6 +2932,11 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
skb_queue_head_init(&chan->tx_q);
+ chan->local_amp_id = 0;
+ chan->move_id = 0;
+ chan->move_state = L2CAP_MOVE_STABLE;
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+
if (chan->mode != L2CAP_MODE_ERTM)
return 0;
@@ -2834,6 +2983,44 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
}
+static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
+ struct l2cap_conf_rfc *rfc)
+{
+ if (chan->local_amp_id && chan->hs_hcon) {
+ u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
+
+ /* Class 1 devices have must have ERTM timeouts
+ * exceeding the Link Supervision Timeout. The
+ * default Link Supervision Timeout for AMP
+ * controllers is 10 seconds.
+ *
+ * Class 1 devices use 0xffffffff for their
+ * best-effort flush timeout, so the clamping logic
+ * will result in a timeout that meets the above
+ * requirement. ERTM timeouts are 16-bit values, so
+ * the maximum timeout is 65.535 seconds.
+ */
+
+ /* Convert timeout to milliseconds and round */
+ ertm_to = DIV_ROUND_UP_ULL(ertm_to, 1000);
+
+ /* This is the recommended formula for class 2 devices
+ * that start ERTM timers when packets are sent to the
+ * controller.
+ */
+ ertm_to = 3 * ertm_to + 500;
+
+ if (ertm_to > 0xffff)
+ ertm_to = 0xffff;
+
+ rfc->retrans_timeout = cpu_to_le16((u16) ertm_to);
+ rfc->monitor_timeout = rfc->retrans_timeout;
+ } else {
+ rfc->retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
+ rfc->monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+ }
+}
+
static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
{
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
@@ -2900,8 +3087,8 @@ done:
case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM;
rfc.max_transmit = chan->max_tx;
- rfc.retrans_timeout = 0;
- rfc.monitor_timeout = 0;
+
+ __l2cap_set_ertm_timeouts(chan, &rfc);
size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
@@ -3129,10 +3316,7 @@ done:
rfc.max_pdu_size = cpu_to_le16(size);
chan->remote_mps = size;
- rfc.retrans_timeout =
- __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
- rfc.monitor_timeout =
- __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+ __l2cap_set_ertm_timeouts(chan, &rfc);
set_bit(CONF_MODE_DONE, &chan->conf_state);
@@ -3308,12 +3492,21 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
struct l2cap_conn_rsp rsp;
struct l2cap_conn *conn = chan->conn;
u8 buf[128];
+ u8 rsp_code;
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
- l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+
+ if (chan->hs_hcon)
+ rsp_code = L2CAP_CREATE_CHAN_RSP;
+ else
+ rsp_code = L2CAP_CONN_RSP;
+
+ BT_DBG("chan %p rsp_code %u", chan, rsp_code);
+
+ l2cap_send_cmd(conn, chan->ident, rsp_code, sizeof(rsp), &rsp);
if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
return;
@@ -3395,8 +3588,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
return 0;
}
-static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
- u8 *data, u8 rsp_code, u8 amp_id)
+static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u8 *data, u8 rsp_code, u8 amp_id)
{
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
@@ -3447,6 +3641,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
bacpy(&bt_sk(sk)->dst, conn->dst);
chan->psm = psm;
chan->dcid = scid;
+ chan->local_amp_id = amp_id;
__l2cap_chan_add(conn, chan);
@@ -3464,8 +3659,17 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
status = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
- result = L2CAP_CR_SUCCESS;
+ /* Force pending result for AMP controllers.
+ * The connection will succeed after the
+ * physical link is up.
+ */
+ if (amp_id) {
+ __l2cap_state_change(chan, BT_CONNECT2);
+ result = L2CAP_CR_PEND;
+ } else {
+ __l2cap_state_change(chan, BT_CONFIG);
+ result = L2CAP_CR_SUCCESS;
+ }
status = L2CAP_CS_NO_INFO;
}
} else {
@@ -3511,6 +3715,8 @@ sendresp:
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
+
+ return chan;
}
static int l2cap_connect_req(struct l2cap_conn *conn,
@@ -3520,7 +3726,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
return 0;
}
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn,
+static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
@@ -3675,6 +3881,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
goto unlock;
}
+ chan->ident = cmd->ident;
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
chan->num_conf_rsp++;
@@ -3714,7 +3921,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
/* check compatibility */
/* Send rsp for BR/EDR channel */
- if (!chan->ctrl_id)
+ if (!chan->hs_hcon)
l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags);
else
chan->ident = cmd->ident;
@@ -3764,13 +3971,15 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
goto done;
}
- /* check compatibility */
-
- if (!chan->ctrl_id)
+ if (!chan->hs_hcon) {
l2cap_send_efs_conf_rsp(chan, buf, cmd->ident,
0);
- else
- chan->ident = cmd->ident;
+ } else {
+ if (l2cap_check_efs(chan)) {
+ amp_create_logical_link(chan);
+ chan->ident = cmd->ident;
+ }
+ }
}
goto done;
@@ -4023,12 +4232,14 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
return 0;
}
-static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd,
- u16 cmd_len, void *data)
+static int l2cap_create_channel_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u16 cmd_len, void *data)
{
struct l2cap_create_chan_req *req = data;
struct l2cap_create_chan_rsp rsp;
+ struct l2cap_chan *chan;
+ struct hci_dev *hdev;
u16 psm, scid;
if (cmd_len != sizeof(*req))
@@ -4042,57 +4253,119 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
- /* Placeholder: Always reject */
+ /* For controller id 0 make BR/EDR connection */
+ if (req->amp_id == HCI_BREDR_ID) {
+ l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
+ req->amp_id);
+ return 0;
+ }
+
+ /* Validate AMP controller id */
+ hdev = hci_dev_get(req->amp_id);
+ if (!hdev)
+ goto error;
+
+ if (hdev->dev_type != HCI_AMP || !test_bit(HCI_UP, &hdev->flags)) {
+ hci_dev_put(hdev);
+ goto error;
+ }
+
+ chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
+ req->amp_id);
+ if (chan) {
+ struct amp_mgr *mgr = conn->hcon->amp_mgr;
+ struct hci_conn *hs_hcon;
+
+ hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
+ if (!hs_hcon) {
+ hci_dev_put(hdev);
+ return -EFAULT;
+ }
+
+ BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
+
+ mgr->bredr_chan = chan;
+ chan->hs_hcon = hs_hcon;
+ chan->fcs = L2CAP_FCS_NONE;
+ conn->mtu = hdev->block_mtu;
+ }
+
+ hci_dev_put(hdev);
+
+ return 0;
+
+error:
rsp.dcid = 0;
rsp.scid = cpu_to_le16(scid);
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM);
+ rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
sizeof(rsp), &rsp);
- return 0;
+ return -EFAULT;
}
-static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd,
- void *data)
+static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
{
- BT_DBG("conn %p", conn);
+ struct l2cap_move_chan_req req;
+ u8 ident;
+
+ BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id);
+
+ ident = l2cap_get_ident(chan->conn);
+ chan->ident = ident;
- return l2cap_connect_rsp(conn, cmd, data);
+ req.icid = cpu_to_le16(chan->scid);
+ req.dest_amp_id = dest_amp_id;
+
+ l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req),
+ &req);
+
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
}
-static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
- u16 icid, u16 result)
+static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result)
{
struct l2cap_move_chan_rsp rsp;
- BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
+ BT_DBG("chan %p, result 0x%4.4x", chan, result);
- rsp.icid = cpu_to_le16(icid);
+ rsp.icid = cpu_to_le16(chan->dcid);
rsp.result = cpu_to_le16(result);
- l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_RSP,
+ sizeof(rsp), &rsp);
}
-static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn,
- struct l2cap_chan *chan,
- u16 icid, u16 result)
+static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result)
{
struct l2cap_move_chan_cfm cfm;
- u8 ident;
- BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
+ BT_DBG("chan %p, result 0x%4.4x", chan, result);
- ident = l2cap_get_ident(conn);
- if (chan)
- chan->ident = ident;
+ chan->ident = l2cap_get_ident(chan->conn);
- cfm.icid = cpu_to_le16(icid);
+ cfm.icid = cpu_to_le16(chan->scid);
cfm.result = cpu_to_le16(result);
- l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm);
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_CFM,
+ sizeof(cfm), &cfm);
+
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+}
+
+static void l2cap_send_move_chan_cfm_icid(struct l2cap_conn *conn, u16 icid)
+{
+ struct l2cap_move_chan_cfm cfm;
+
+ BT_DBG("conn %p, icid 0x%4.4x", conn, icid);
+
+ cfm.icid = cpu_to_le16(icid);
+ cfm.result = __constant_cpu_to_le16(L2CAP_MC_UNCONFIRMED);
+
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_MOVE_CHAN_CFM,
+ sizeof(cfm), &cfm);
}
static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
@@ -4106,11 +4379,289 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
}
+static void __release_logical_link(struct l2cap_chan *chan)
+{
+ chan->hs_hchan = NULL;
+ chan->hs_hcon = NULL;
+
+ /* Placeholder - release the logical link */
+}
+
+static void l2cap_logical_fail(struct l2cap_chan *chan)
+{
+ /* Logical link setup failed */
+ if (chan->state != BT_CONNECTED) {
+ /* Create channel failure, disconnect */
+ l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ return;
+ }
+
+ switch (chan->move_role) {
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ l2cap_move_done(chan);
+ l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_SUPP);
+ break;
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP ||
+ chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) {
+ /* Remote has only sent pending or
+ * success responses, clean up
+ */
+ l2cap_move_done(chan);
+ }
+
+ /* Other amp move states imply that the move
+ * has already aborted
+ */
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
+ break;
+ }
+}
+
+static void l2cap_logical_finish_create(struct l2cap_chan *chan,
+ struct hci_chan *hchan)
+{
+ struct l2cap_conf_rsp rsp;
+
+ chan->hs_hchan = hchan;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ l2cap_send_efs_conf_rsp(chan, &rsp, chan->ident, 0);
+
+ if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
+ int err;
+
+ set_default_fcs(chan);
+
+ err = l2cap_ertm_init(chan);
+ if (err < 0)
+ l2cap_send_disconn_req(chan->conn, chan, -err);
+ else
+ l2cap_chan_ready(chan);
+ }
+}
+
+static void l2cap_logical_finish_move(struct l2cap_chan *chan,
+ struct hci_chan *hchan)
+{
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ BT_DBG("move_state %d", chan->move_state);
+
+ switch (chan->move_state) {
+ case L2CAP_MOVE_WAIT_LOGICAL_COMP:
+ /* Move confirm will be sent after a success
+ * response is received
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+ break;
+ case L2CAP_MOVE_WAIT_LOGICAL_CFM:
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ } else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
+ } else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS);
+ }
+ break;
+ default:
+ /* Move was not in expected state, free the channel */
+ __release_logical_link(chan);
+
+ chan->move_state = L2CAP_MOVE_STABLE;
+ }
+}
+
+/* Call with chan locked */
+void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
+ u8 status)
+{
+ BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status);
+
+ if (status) {
+ l2cap_logical_fail(chan);
+ __release_logical_link(chan);
+ return;
+ }
+
+ if (chan->state != BT_CONNECTED) {
+ /* Ignore logical link if channel is on BR/EDR */
+ if (chan->local_amp_id)
+ l2cap_logical_finish_create(chan, hchan);
+ } else {
+ l2cap_logical_finish_move(chan, hchan);
+ }
+}
+
+void l2cap_move_start(struct l2cap_chan *chan)
+{
+ BT_DBG("chan %p", chan);
+
+ if (chan->local_amp_id == HCI_BREDR_ID) {
+ if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
+ return;
+ chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
+ chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
+ /* Placeholder - start physical link setup */
+ } else {
+ chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
+ chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+ chan->move_id = 0;
+ l2cap_move_setup(chan);
+ l2cap_send_move_chan_req(chan, 0);
+ }
+}
+
+static void l2cap_do_create(struct l2cap_chan *chan, int result,
+ u8 local_amp_id, u8 remote_amp_id)
+{
+ BT_DBG("chan %p state %s %u -> %u", chan, state_to_string(chan->state),
+ local_amp_id, remote_amp_id);
+
+ chan->fcs = L2CAP_FCS_NONE;
+
+ /* Outgoing channel on AMP */
+ if (chan->state == BT_CONNECT) {
+ if (result == L2CAP_CR_SUCCESS) {
+ chan->local_amp_id = local_amp_id;
+ l2cap_send_create_chan_req(chan, remote_amp_id);
+ } else {
+ /* Revert to BR/EDR connect */
+ l2cap_send_conn_req(chan);
+ }
+
+ return;
+ }
+
+ /* Incoming channel on AMP */
+ if (__l2cap_no_conn_pending(chan)) {
+ struct l2cap_conn_rsp rsp;
+ char buf[128];
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
+
+ if (result == L2CAP_CR_SUCCESS) {
+ /* Send successful response */
+ rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ } else {
+ /* Send negative response */
+ rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM);
+ rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ }
+
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP,
+ sizeof(rsp), &rsp);
+
+ if (result == L2CAP_CR_SUCCESS) {
+ __l2cap_state_change(chan, BT_CONFIG);
+ set_bit(CONF_REQ_SENT, &chan->conf_state);
+ l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
+ L2CAP_CONF_REQ,
+ l2cap_build_conf_req(chan, buf), buf);
+ chan->num_conf_req++;
+ }
+ }
+}
+
+static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id,
+ u8 remote_amp_id)
+{
+ l2cap_move_setup(chan);
+ chan->move_id = local_amp_id;
+ chan->move_state = L2CAP_MOVE_WAIT_RSP;
+
+ l2cap_send_move_chan_req(chan, remote_amp_id);
+}
+
+static void l2cap_do_move_respond(struct l2cap_chan *chan, int result)
+{
+ struct hci_chan *hchan = NULL;
+
+ /* Placeholder - get hci_chan for logical link */
+
+ if (hchan) {
+ if (hchan->state == BT_CONNECTED) {
+ /* Logical link is ready to go */
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS);
+
+ l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
+ } else {
+ /* Wait for logical link to be ready */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ }
+ } else {
+ /* Logical link not available */
+ l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_ALLOWED);
+ }
+}
+
+static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result)
+{
+ if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
+ u8 rsp_result;
+ if (result == -EINVAL)
+ rsp_result = L2CAP_MR_BAD_ID;
+ else
+ rsp_result = L2CAP_MR_NOT_ALLOWED;
+
+ l2cap_send_move_chan_rsp(chan, rsp_result);
+ }
+
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ chan->move_state = L2CAP_MOVE_STABLE;
+
+ /* Restart data transmission */
+ l2cap_ertm_send(chan);
+}
+
+/* Invoke with locked chan */
+void __l2cap_physical_cfm(struct l2cap_chan *chan, int result)
+{
+ u8 local_amp_id = chan->local_amp_id;
+ u8 remote_amp_id = chan->remote_amp_id;
+
+ BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d",
+ chan, result, local_amp_id, remote_amp_id);
+
+ if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) {
+ l2cap_chan_unlock(chan);
+ return;
+ }
+
+ if (chan->state != BT_CONNECTED) {
+ l2cap_do_create(chan, result, local_amp_id, remote_amp_id);
+ } else if (result != L2CAP_MR_SUCCESS) {
+ l2cap_do_move_cancel(chan, result);
+ } else {
+ switch (chan->move_role) {
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ l2cap_do_move_initiate(chan, local_amp_id,
+ remote_amp_id);
+ break;
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ l2cap_do_move_respond(chan, result);
+ break;
+ default:
+ l2cap_do_move_cancel(chan, result);
+ break;
+ }
+ }
+}
+
static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_req *req = data;
+ struct l2cap_move_chan_rsp rsp;
+ struct l2cap_chan *chan;
u16 icid = 0;
u16 result = L2CAP_MR_NOT_ALLOWED;
@@ -4124,15 +4675,206 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
if (!enable_hs)
return -EINVAL;
- /* Placeholder: Always refuse */
- l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
+ chan = l2cap_get_chan_by_dcid(conn, icid);
+ if (!chan) {
+ rsp.icid = cpu_to_le16(icid);
+ rsp.result = __constant_cpu_to_le16(L2CAP_MR_NOT_ALLOWED);
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_MOVE_CHAN_RSP,
+ sizeof(rsp), &rsp);
+ return 0;
+ }
+
+ chan->ident = cmd->ident;
+
+ if (chan->scid < L2CAP_CID_DYN_START ||
+ chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY ||
+ (chan->mode != L2CAP_MODE_ERTM &&
+ chan->mode != L2CAP_MODE_STREAMING)) {
+ result = L2CAP_MR_NOT_ALLOWED;
+ goto send_move_response;
+ }
+
+ if (chan->local_amp_id == req->dest_amp_id) {
+ result = L2CAP_MR_SAME_ID;
+ goto send_move_response;
+ }
+
+ if (req->dest_amp_id) {
+ struct hci_dev *hdev;
+ hdev = hci_dev_get(req->dest_amp_id);
+ if (!hdev || hdev->dev_type != HCI_AMP ||
+ !test_bit(HCI_UP, &hdev->flags)) {
+ if (hdev)
+ hci_dev_put(hdev);
+
+ result = L2CAP_MR_BAD_ID;
+ goto send_move_response;
+ }
+ hci_dev_put(hdev);
+ }
+
+ /* Detect a move collision. Only send a collision response
+ * if this side has "lost", otherwise proceed with the move.
+ * The winner has the larger bd_addr.
+ */
+ if ((__chan_is_moving(chan) ||
+ chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
+ bacmp(conn->src, conn->dst) > 0) {
+ result = L2CAP_MR_COLLISION;
+ goto send_move_response;
+ }
+
+ chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+ l2cap_move_setup(chan);
+ chan->move_id = req->dest_amp_id;
+ icid = chan->dcid;
+
+ if (!req->dest_amp_id) {
+ /* Moving to BR/EDR */
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ result = L2CAP_MR_PEND;
+ } else {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ result = L2CAP_MR_SUCCESS;
+ }
+ } else {
+ chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
+ /* Placeholder - uncomment when amp functions are available */
+ /*amp_accept_physical(chan, req->dest_amp_id);*/
+ result = L2CAP_MR_PEND;
+ }
+
+send_move_response:
+ l2cap_send_move_chan_rsp(chan, result);
+
+ l2cap_chan_unlock(chan);
return 0;
}
-static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd,
- u16 cmd_len, void *data)
+static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result)
+{
+ struct l2cap_chan *chan;
+ struct hci_chan *hchan = NULL;
+
+ chan = l2cap_get_chan_by_scid(conn, icid);
+ if (!chan) {
+ l2cap_send_move_chan_cfm_icid(conn, icid);
+ return;
+ }
+
+ __clear_chan_timer(chan);
+ if (result == L2CAP_MR_PEND)
+ __set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT);
+
+ switch (chan->move_state) {
+ case L2CAP_MOVE_WAIT_LOGICAL_COMP:
+ /* Move confirm will be sent when logical link
+ * is complete.
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ break;
+ case L2CAP_MOVE_WAIT_RSP_SUCCESS:
+ if (result == L2CAP_MR_PEND) {
+ break;
+ } else if (test_bit(CONN_LOCAL_BUSY,
+ &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ } else {
+ /* Logical link is up or moving to BR/EDR,
+ * proceed with move
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
+ }
+ break;
+ case L2CAP_MOVE_WAIT_RSP:
+ /* Moving to AMP */
+ if (result == L2CAP_MR_SUCCESS) {
+ /* Remote is ready, send confirm immediately
+ * after logical link is ready
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ } else {
+ /* Both logical link and move success
+ * are required to confirm
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_COMP;
+ }
+
+ /* Placeholder - get hci_chan for logical link */
+ if (!hchan) {
+ /* Logical link not available */
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
+ break;
+ }
+
+ /* If the logical link is not yet connected, do not
+ * send confirmation.
+ */
+ if (hchan->state != BT_CONNECTED)
+ break;
+
+ /* Logical link is already ready to go */
+
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ if (result == L2CAP_MR_SUCCESS) {
+ /* Can confirm now */
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
+ } else {
+ /* Now only need move success
+ * to confirm
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+ }
+
+ l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
+ break;
+ default:
+ /* Any other amp move state means the move failed. */
+ chan->move_id = chan->local_amp_id;
+ l2cap_move_done(chan);
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
+ }
+
+ l2cap_chan_unlock(chan);
+}
+
+static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
+ u16 result)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_get_chan_by_ident(conn, ident);
+ if (!chan) {
+ /* Could not locate channel, icid is best guess */
+ l2cap_send_move_chan_cfm_icid(conn, icid);
+ return;
+ }
+
+ __clear_chan_timer(chan);
+
+ if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
+ if (result == L2CAP_MR_COLLISION) {
+ chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+ } else {
+ /* Cleanup - cancel move */
+ chan->move_id = chan->local_amp_id;
+ l2cap_move_done(chan);
+ }
+ }
+
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
+
+ l2cap_chan_unlock(chan);
+}
+
+static int l2cap_move_channel_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u16 cmd_len, void *data)
{
struct l2cap_move_chan_rsp *rsp = data;
u16 icid, result;
@@ -4145,17 +4887,20 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
- /* Placeholder: Always unconfirmed */
- l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED);
+ if (result == L2CAP_MR_SUCCESS || result == L2CAP_MR_PEND)
+ l2cap_move_continue(conn, icid, result);
+ else
+ l2cap_move_fail(conn, cmd->ident, icid, result);
return 0;
}
-static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd,
- u16 cmd_len, void *data)
+static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u16 cmd_len, void *data)
{
struct l2cap_move_chan_cfm *cfm = data;
+ struct l2cap_chan *chan;
u16 icid, result;
if (cmd_len != sizeof(*cfm))
@@ -4166,8 +4911,29 @@ static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
+ chan = l2cap_get_chan_by_dcid(conn, icid);
+ if (!chan) {
+ /* Spec requires a response even if the icid was not found */
+ l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
+ return 0;
+ }
+
+ if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
+ if (result == L2CAP_MC_CONFIRMED) {
+ chan->local_amp_id = chan->move_id;
+ if (!chan->local_amp_id)
+ __release_logical_link(chan);
+ } else {
+ chan->move_id = chan->local_amp_id;
+ }
+
+ l2cap_move_done(chan);
+ }
+
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
+ l2cap_chan_unlock(chan);
+
return 0;
}
@@ -4176,6 +4942,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_cfm_rsp *rsp = data;
+ struct l2cap_chan *chan;
u16 icid;
if (cmd_len != sizeof(*rsp))
@@ -4185,6 +4952,23 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x", icid);
+ chan = l2cap_get_chan_by_scid(conn, icid);
+ if (!chan)
+ return 0;
+
+ __clear_chan_timer(chan);
+
+ if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
+ chan->local_amp_id = chan->move_id;
+
+ if (!chan->local_amp_id && chan->hs_hchan)
+ __release_logical_link(chan);
+
+ l2cap_move_done(chan);
+ }
+
+ l2cap_chan_unlock(chan);
+
return 0;
}
@@ -4269,7 +5053,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
case L2CAP_CONN_RSP:
case L2CAP_CREATE_CHAN_RSP:
- err = l2cap_connect_rsp(conn, cmd, data);
+ err = l2cap_connect_create_rsp(conn, cmd, data);
break;
case L2CAP_CONF_REQ:
@@ -4556,6 +5340,12 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
return err;
}
+static int l2cap_resegment(struct l2cap_chan *chan)
+{
+ /* Placeholder */
+ return 0;
+}
+
void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
{
u8 event;
@@ -4871,8 +5661,8 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
if (control->final) {
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
- if (!test_and_clear_bit(CONN_REJ_ACT,
- &chan->conn_state)) {
+ if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state) &&
+ !__chan_is_moving(chan)) {
control->final = 0;
l2cap_retransmit_all(chan, control);
}
@@ -5061,6 +5851,96 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
return err;
}
+static int l2cap_finish_move(struct l2cap_chan *chan)
+{
+ BT_DBG("chan %p", chan);
+
+ chan->rx_state = L2CAP_RX_STATE_RECV;
+
+ if (chan->hs_hcon)
+ chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
+ else
+ chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
+
+ return l2cap_resegment(chan);
+}
+
+static int l2cap_rx_state_wait_p(struct l2cap_chan *chan,
+ struct l2cap_ctrl *control,
+ struct sk_buff *skb, u8 event)
+{
+ int err;
+
+ BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
+ event);
+
+ if (!control->poll)
+ return -EPROTO;
+
+ l2cap_process_reqseq(chan, control->reqseq);
+
+ if (!skb_queue_empty(&chan->tx_q))
+ chan->tx_send_head = skb_peek(&chan->tx_q);
+ else
+ chan->tx_send_head = NULL;
+
+ /* Rewind next_tx_seq to the point expected
+ * by the receiver.
+ */
+ chan->next_tx_seq = control->reqseq;
+ chan->unacked_frames = 0;
+
+ err = l2cap_finish_move(chan);
+ if (err)
+ return err;
+
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
+ l2cap_send_i_or_rr_or_rnr(chan);
+
+ if (event == L2CAP_EV_RECV_IFRAME)
+ return -EPROTO;
+
+ return l2cap_rx_state_recv(chan, control, NULL, event);
+}
+
+static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,
+ struct l2cap_ctrl *control,
+ struct sk_buff *skb, u8 event)
+{
+ int err;
+
+ if (!control->final)
+ return -EPROTO;
+
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+
+ chan->rx_state = L2CAP_RX_STATE_RECV;
+ l2cap_process_reqseq(chan, control->reqseq);
+
+ if (!skb_queue_empty(&chan->tx_q))
+ chan->tx_send_head = skb_peek(&chan->tx_q);
+ else
+ chan->tx_send_head = NULL;
+
+ /* Rewind next_tx_seq to the point expected
+ * by the receiver.
+ */
+ chan->next_tx_seq = control->reqseq;
+ chan->unacked_frames = 0;
+
+ if (chan->hs_hcon)
+ chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
+ else
+ chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
+
+ err = l2cap_resegment(chan);
+
+ if (!err)
+ err = l2cap_rx_state_recv(chan, control, skb, event);
+
+ return err;
+}
+
static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq)
{
/* Make sure reqseq is for a packet that has been sent but not acked */
@@ -5087,6 +5967,12 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
err = l2cap_rx_state_srej_sent(chan, control, skb,
event);
break;
+ case L2CAP_RX_STATE_WAIT_P:
+ err = l2cap_rx_state_wait_p(chan, control, skb, event);
+ break;
+ case L2CAP_RX_STATE_WAIT_F:
+ err = l2cap_rx_state_wait_f(chan, control, skb, event);
+ break;
default:
/* shut it down */
break;
@@ -5206,7 +6092,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
control->super);
if (len != 0) {
- BT_ERR("%d", len);
+ BT_ERR("Trailing bytes: %d in sframe", len);
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
}
@@ -5422,9 +6308,9 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
conn = l2cap_conn_add(hcon, status);
if (conn)
l2cap_conn_ready(conn);
- } else
+ } else {
l2cap_conn_del(hcon, bt_to_errno(status));
-
+ }
}
int l2cap_disconn_ind(struct hci_conn *hcon)
@@ -5500,7 +6386,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
continue;
}
- if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
+ if (!__l2cap_no_conn_pending(chan)) {
l2cap_chan_unlock(chan);
continue;
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 89f1472939ec..1bcfb8422fdc 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -736,6 +736,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
}
chan->chan_policy = (u8) opt;
+
+ if (sk->sk_state == BT_CONNECTED &&
+ chan->move_role == L2CAP_MOVE_ROLE_NONE)
+ l2cap_move_start(chan);
+
break;
default:
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 158a87bb0c0d..142764aec2af 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -222,7 +222,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
+ hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_STATUS);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev));
@@ -253,7 +253,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
+ hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
@@ -377,15 +377,15 @@ static u32 get_supported_settings(struct hci_dev *hdev)
u32 settings = 0;
settings |= MGMT_SETTING_POWERED;
- settings |= MGMT_SETTING_CONNECTABLE;
- settings |= MGMT_SETTING_FAST_CONNECTABLE;
- settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_PAIRABLE;
if (lmp_ssp_capable(hdev))
settings |= MGMT_SETTING_SSP;
if (lmp_bredr_capable(hdev)) {
+ settings |= MGMT_SETTING_CONNECTABLE;
+ settings |= MGMT_SETTING_FAST_CONNECTABLE;
+ settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_BREDR;
settings |= MGMT_SETTING_LINK_SECURITY;
}
@@ -485,7 +485,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
ptr += (name_len + 2);
}
- if (hdev->inq_tx_power) {
+ if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) {
ptr[0] = 2;
ptr[1] = EIR_TX_POWER;
ptr[2] = (u8) hdev->inq_tx_power;
@@ -566,7 +566,7 @@ static int update_eir(struct hci_dev *hdev)
if (!hdev_is_powered(hdev))
return 0;
- if (!(hdev->features[6] & LMP_EXT_INQ))
+ if (!lmp_ext_inq_capable(hdev))
return 0;
if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
@@ -833,7 +833,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
if (hdev)
hdr->index = cpu_to_le16(hdev->id);
else
- hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
+ hdr->index = __constant_cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
@@ -868,6 +868,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name);
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+ MGMT_STATUS_NOT_SUPPORTED);
+
timeout = __le16_to_cpu(cp->timeout);
if (!cp->val && timeout > 0)
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
@@ -963,6 +967,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name);
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+ MGMT_STATUS_NOT_SUPPORTED);
+
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
@@ -1061,6 +1069,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name);
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
+ MGMT_STATUS_NOT_SUPPORTED);
+
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
@@ -1214,7 +1226,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
}
val = !!cp->val;
- enabled = !!(hdev->host_features[0] & LMP_HOST_LE);
+ enabled = !!lmp_host_le_capable(hdev);
if (!hdev_is_powered(hdev) || val == enabled) {
bool changed = false;
@@ -1250,7 +1262,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
if (val) {
hci_cp.le = val;
- hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+ hci_cp.simul = !!lmp_le_br_capable(hdev);
}
err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
@@ -2596,6 +2608,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
BT_DBG("%s", hdev->name);
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_STATUS_NOT_SUPPORTED);
+
if (!hdev_is_powered(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_NOT_POWERED);
@@ -2873,6 +2889,21 @@ static void settings_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_free(cmd);
}
+static int set_bredr_scan(struct hci_dev *hdev)
+{
+ u8 scan = 0;
+
+ if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ scan |= SCAN_PAGE;
+ if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+ scan |= SCAN_INQUIRY;
+
+ if (!scan)
+ return 0;
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+}
+
int mgmt_powered(struct hci_dev *hdev, u8 powered)
{
struct cmd_lookup match = { NULL, hdev };
@@ -2884,17 +2915,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
if (powered) {
- u8 scan = 0;
-
- if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- scan |= SCAN_PAGE;
- if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- scan |= SCAN_INQUIRY;
-
- if (scan)
- hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
-
- if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
+ !lmp_host_ssp_capable(hdev)) {
u8 ssp = 1;
hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
@@ -2904,15 +2926,24 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
struct hci_cp_write_le_host_supported cp;
cp.le = 1;
- cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
-
- hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
- sizeof(cp), &cp);
+ cp.simul = !!lmp_le_br_capable(hdev);
+
+ /* Check first if we already have the right
+ * host state (host features set)
+ */
+ if (cp.le != !!lmp_host_le_capable(hdev) ||
+ cp.simul != !!lmp_host_le_br_capable(hdev))
+ hci_send_cmd(hdev,
+ HCI_OP_WRITE_LE_HOST_SUPPORTED,
+ sizeof(cp), &cp);
}
- update_class(hdev);
- update_name(hdev, hdev->dev_name);
- update_eir(hdev);
+ if (lmp_bredr_capable(hdev)) {
+ set_bredr_scan(hdev);
+ update_class(hdev);
+ update_name(hdev, hdev->dev_name);
+ update_eir(hdev);
+ }
} else {
u8 status = MGMT_STATUS_NOT_POWERED;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
@@ -3361,7 +3392,7 @@ static int clear_eir(struct hci_dev *hdev)
{
struct hci_cp_write_eir cp;
- if (!(hdev->features[6] & LMP_EXT_INQ))
+ if (!lmp_ext_inq_capable(hdev))
return 0;
memset(hdev->eir, 0, sizeof(hdev->eir));
@@ -3493,7 +3524,12 @@ send_event:
err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev,
sizeof(ev), cmd ? cmd->sk : NULL);
- update_eir(hdev);
+ /* EIR is taken care of separately when powering on the
+ * adapter so only update them here if this is a name change
+ * unrelated to power on.
+ */
+ if (!test_bit(HCI_INIT, &hdev->flags))
+ update_eir(hdev);
failed:
if (cmd)
@@ -3588,9 +3624,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev->addr.type = link_to_bdaddr(link_type, addr_type);
ev->rssi = rssi;
if (cfm_name)
- ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
+ ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
if (!ssp)
- ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
+ ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
if (eir_len > 0)
memcpy(ev->eir, eir, eir_len);
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c
index 493353534a0f..537488cbf941 100644
--- a/net/mac80211/aes_cmac.c
+++ b/net/mac80211/aes_cmac.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/crypto.h>
+#include <linux/export.h>
#include <linux/err.h>
#include <crypto/aes.h>
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 186d9919b043..808338a1bce5 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -118,7 +118,7 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
return;
}
- for (i = 0; i < STA_TID_NUM; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
if (ba_rx_bitmap & BIT(i))
set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 3195a6307f50..4152ed1034b8 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -448,7 +448,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
if (WARN_ON(!local->ops->ampdu_action))
return -EINVAL;
- if ((tid >= STA_TID_NUM) ||
+ if ((tid >= IEEE80211_NUM_TIDS) ||
!(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) ||
(local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW))
return -EINVAL;
@@ -605,9 +605,9 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
trace_api_start_tx_ba_cb(sdata, ra, tid);
- if (tid >= STA_TID_NUM) {
+ if (tid >= IEEE80211_NUM_TIDS) {
ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n",
- tid, STA_TID_NUM);
+ tid, IEEE80211_NUM_TIDS);
return;
}
@@ -687,7 +687,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
if (!local->ops->ampdu_action)
return -EINVAL;
- if (tid >= STA_TID_NUM)
+ if (tid >= IEEE80211_NUM_TIDS)
return -EINVAL;
spin_lock_bh(&sta->lock);
@@ -722,9 +722,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
trace_api_stop_tx_ba_cb(sdata, ra, tid);
- if (tid >= STA_TID_NUM) {
+ if (tid >= IEEE80211_NUM_TIDS) {
ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n",
- tid, STA_TID_NUM);
+ tid, IEEE80211_NUM_TIDS);
return;
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 76690020d605..4965aa6424ec 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -370,30 +370,32 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
return 0;
}
-static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx)
-{
- enum ieee80211_band band = ieee80211_get_sdata_band(sta->sdata);
-
- if (!(rate->flags & RATE_INFO_FLAGS_MCS)) {
- struct ieee80211_supported_band *sband;
- sband = sta->local->hw.wiphy->bands[band];
- rate->legacy = sband->bitrates[idx].bitrate;
- } else
- rate->mcs = idx;
-}
-
void sta_set_rate_info_tx(struct sta_info *sta,
const struct ieee80211_tx_rate *rate,
struct rate_info *rinfo)
{
rinfo->flags = 0;
- if (rate->flags & IEEE80211_TX_RC_MCS)
+ if (rate->flags & IEEE80211_TX_RC_MCS) {
rinfo->flags |= RATE_INFO_FLAGS_MCS;
+ rinfo->mcs = rate->idx;
+ } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+ rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+ rinfo->mcs = ieee80211_rate_get_vht_mcs(rate);
+ rinfo->nss = ieee80211_rate_get_vht_nss(rate);
+ } else {
+ struct ieee80211_supported_band *sband;
+ sband = sta->local->hw.wiphy->bands[
+ ieee80211_get_sdata_band(sta->sdata)];
+ rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+ }
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+ rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
- rate_idx_to_bitrate(rinfo, sta, rate->idx);
}
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
@@ -444,13 +446,32 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
sinfo->rxrate.flags = 0;
- if (sta->last_rx_rate_flag & RX_FLAG_HT)
+ if (sta->last_rx_rate_flag & RX_FLAG_HT) {
sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS;
+ sinfo->rxrate.mcs = sta->last_rx_rate_idx;
+ } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
+ sinfo->rxrate.nss = sta->last_rx_rate_vht_nss;
+ sinfo->rxrate.mcs = sta->last_rx_rate_idx;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+ sband = sta->local->hw.wiphy->bands[
+ ieee80211_get_sdata_band(sta->sdata)];
+ sinfo->rxrate.legacy =
+ sband->bitrates[sta->last_rx_rate_idx].bitrate;
+ }
+
if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
- rate_idx_to_bitrate(&sinfo->rxrate, sta, sta->last_rx_rate_idx);
+ if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
+ if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
@@ -615,7 +636,7 @@ do_survey:
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf)
- channel = chanctx_conf->channel;
+ channel = chanctx_conf->def.chan;
else
channel = NULL;
rcu_read_unlock();
@@ -735,15 +756,13 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+ struct cfg80211_chan_def *chandef)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata;
int ret = 0;
- if (local->monitor_channel == chan &&
- local->monitor_channel_type == channel_type)
+ if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
return 0;
mutex_lock(&local->iflist_mtx);
@@ -753,20 +772,17 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
lockdep_is_held(&local->iflist_mtx));
if (sdata) {
ieee80211_vif_release_channel(sdata);
- ret = ieee80211_vif_use_channel(
- sdata, chan, channel_type,
+ ret = ieee80211_vif_use_channel(sdata, chandef,
IEEE80211_CHANCTX_EXCLUSIVE);
}
} else if (local->open_count == local->monitors) {
- local->_oper_channel = chan;
- local->_oper_channel_type = channel_type;
+ local->_oper_channel = chandef->chan;
+ local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
ieee80211_hw_config(local, 0);
}
- if (ret == 0) {
- local->monitor_channel = chan;
- local->monitor_channel_type = channel_type;
- }
+ if (ret == 0)
+ local->monitor_chandef = *chandef;
mutex_unlock(&local->iflist_mtx);
return ret;
@@ -888,8 +904,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
sdata->smps_mode = IEEE80211_SMPS_OFF;
sdata->needed_rx_chains = sdata->local->rx_chains;
- err = ieee80211_vif_use_channel(sdata, params->channel,
- params->channel_type,
+ err = ieee80211_vif_use_channel(sdata, &params->chandef,
IEEE80211_CHANCTX_SHARED);
if (err)
return err;
@@ -922,6 +937,15 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
return err;
changed |= err;
+ err = drv_start_ap(sdata->local, sdata);
+ if (err) {
+ old = rtnl_dereference(sdata->u.ap.beacon);
+ if (old)
+ kfree_rcu(old, rcu_head);
+ RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+ return err;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
netif_carrier_on(dev);
@@ -953,26 +977,38 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
{
- struct ieee80211_sub_if_data *sdata, *vlan;
- struct beacon_data *old;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *vlan;
+ struct ieee80211_local *local = sdata->local;
+ struct beacon_data *old_beacon;
+ struct probe_resp *old_probe_resp;
- old = rtnl_dereference(sdata->u.ap.beacon);
- if (!old)
+ old_beacon = rtnl_dereference(sdata->u.ap.beacon);
+ if (!old_beacon)
return -ENOENT;
+ old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+ /* turn off carrier for this interface and dependent VLANs */
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
netif_carrier_off(vlan->dev);
netif_carrier_off(dev);
+ /* remove beacon and probe response */
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+ RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
+ kfree_rcu(old_beacon, rcu_head);
+ if (old_probe_resp)
+ kfree_rcu(old_probe_resp, rcu_head);
- kfree_rcu(old, rcu_head);
-
- sta_info_flush(sdata->local, sdata);
+ sta_info_flush(local, sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
+ drv_stop_ap(sdata->local, sdata);
+
+ /* free all potentially still buffered bcast frames */
+ local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
+ skb_queue_purge(&sdata->u.ap.ps.bc_buf);
+
ieee80211_vif_release_channel(sdata);
return 0;
@@ -1686,8 +1722,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
sdata->smps_mode = IEEE80211_SMPS_OFF;
sdata->needed_rx_chains = sdata->local->rx_chains;
- err = ieee80211_vif_use_channel(sdata, setup->channel,
- setup->channel_type,
+ err = ieee80211_vif_use_channel(sdata, &setup->chandef,
IEEE80211_CHANCTX_SHARED);
if (err)
return err;
@@ -1933,6 +1968,16 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
}
+static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
+ int rate[IEEE80211_NUM_BANDS])
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ memcpy(sdata->vif.bss_conf.mcast_rate, rate, sizeof(rate));
+
+ return 0;
+}
+
static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
@@ -1959,10 +2004,16 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
return err;
}
- if (changed & WIPHY_PARAM_RETRY_SHORT)
+ if (changed & WIPHY_PARAM_RETRY_SHORT) {
+ if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY)
+ return -EINVAL;
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
- if (changed & WIPHY_PARAM_RETRY_LONG)
+ }
+ if (changed & WIPHY_PARAM_RETRY_LONG) {
+ if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY)
+ return -EINVAL;
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
+ }
if (changed &
(WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
@@ -1971,45 +2022,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
}
static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, int mbm)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_channel *chan = local->_oper_channel;
- u32 changes = 0;
+ struct ieee80211_sub_if_data *sdata;
- /* FIXME */
- if (local->use_chanctx)
- return -EOPNOTSUPP;
+ if (wdev) {
+ sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ case NL80211_TX_POWER_FIXED:
+ if (mbm < 0 || (mbm % 100))
+ return -EOPNOTSUPP;
+ sdata->user_power_level = MBM_TO_DBM(mbm);
+ break;
+ }
+
+ ieee80211_recalc_txpower(sdata);
+
+ return 0;
+ }
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
- local->user_power_level = -1;
+ local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
break;
case NL80211_TX_POWER_LIMITED:
- if (mbm < 0 || (mbm % 100))
- return -EOPNOTSUPP;
- local->user_power_level = MBM_TO_DBM(mbm);
- break;
case NL80211_TX_POWER_FIXED:
if (mbm < 0 || (mbm % 100))
return -EOPNOTSUPP;
- /* TODO: move to cfg80211 when it knows the channel */
- if (MBM_TO_DBM(mbm) > chan->max_power)
- return -EINVAL;
local->user_power_level = MBM_TO_DBM(mbm);
break;
}
- ieee80211_hw_config(local, changes);
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list)
+ sdata->user_power_level = local->user_power_level;
+ list_for_each_entry(sdata, &local->interfaces, list)
+ ieee80211_recalc_txpower(sdata);
+ mutex_unlock(&local->iflist_mtx);
return 0;
}
-static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
+static int ieee80211_get_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ int *dbm)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- *dbm = local->hw.conf.power_level;
+ if (!local->use_chanctx)
+ *dbm = local->hw.conf.power_level;
+ else
+ *dbm = sdata->vif.bss_conf.txpower;
return 0;
}
@@ -2078,7 +2149,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
* the new value until we associate.
*/
if (!sdata->u.mgd.associated ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
ap = sdata->u.mgd.associated->bssid;
@@ -2185,7 +2256,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
static int ieee80211_start_roc_work(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
unsigned int duration, u64 *cookie,
struct sk_buff *txskb)
{
@@ -2203,7 +2273,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
return -ENOMEM;
roc->chan = channel;
- roc->chan_type = channel_type;
roc->duration = duration;
roc->req_duration = duration;
roc->frame = txskb;
@@ -2236,7 +2305,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
if (!duration)
duration = 10;
- ret = drv_remain_on_channel(local, channel, channel_type, duration);
+ ret = drv_remain_on_channel(local, sdata, channel, duration);
if (ret) {
kfree(roc);
return ret;
@@ -2247,7 +2316,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
out_check_combine:
list_for_each_entry(tmp, &local->roc_list, list) {
- if (tmp->chan != channel || tmp->chan_type != channel_type)
+ if (tmp->chan != channel || tmp->sdata != sdata)
continue;
/*
@@ -2341,13 +2410,22 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
list_add_tail(&roc->list, &local->roc_list);
/*
- * cookie is either the roc (for normal roc)
+ * cookie is either the roc cookie (for normal roc)
* or the SKB (for mgmt TX)
*/
- if (txskb)
+ if (!txskb) {
+ /* local->mtx protects this */
+ local->roc_cookie_counter++;
+ roc->cookie = local->roc_cookie_counter;
+ /* wow, you wrapped 64 bits ... more likely a bug */
+ if (WARN_ON(roc->cookie == 0)) {
+ roc->cookie = 1;
+ local->roc_cookie_counter++;
+ }
+ *cookie = roc->cookie;
+ } else {
*cookie = (unsigned long)txskb;
- else
- *cookie = (unsigned long)roc;
+ }
return 0;
}
@@ -2355,7 +2433,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
static int ieee80211_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie)
{
@@ -2364,7 +2441,7 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
int ret;
mutex_lock(&local->mtx);
- ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+ ret = ieee80211_start_roc_work(local, sdata, chan,
duration, cookie, NULL);
mutex_unlock(&local->mtx);
@@ -2382,7 +2459,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
struct ieee80211_roc_work *dep, *tmp2;
list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
- if (!mgmt_tx && (unsigned long)dep != cookie)
+ if (!mgmt_tx && dep->cookie != cookie)
continue;
else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
continue;
@@ -2394,7 +2471,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
return 0;
}
- if (!mgmt_tx && (unsigned long)roc != cookie)
+ if (!mgmt_tx && roc->cookie != cookie)
continue;
else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
continue;
@@ -2457,10 +2534,8 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck,
- bool dont_wait_for_ack, u64 *cookie)
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct ieee80211_local *local = sdata->local;
@@ -2529,14 +2604,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (chanctx_conf) {
- need_offchan = chan != chanctx_conf->channel;
- if (channel_type_valid &&
- channel_type != chanctx_conf->channel_type)
- need_offchan = true;
- } else {
+ if (chanctx_conf)
+ need_offchan = chan != chanctx_conf->def.chan;
+ else
need_offchan = true;
- }
rcu_read_unlock();
}
@@ -2571,7 +2642,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
local->hw.offchannel_tx_hw_queue;
/* This will handle all kinds of coalescing and immediate TX */
- ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+ ret = ieee80211_start_roc_work(local, sdata, chan,
wait, cookie, skb);
if (ret)
kfree_skb(skb);
@@ -3005,7 +3076,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
rcu_read_unlock();
return -EINVAL;
}
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
sta = sta_info_get(sdata, peer);
if (sta) {
qos = test_sta_flag(sta, WLAN_STA_WME);
@@ -3062,23 +3133,23 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
-static struct ieee80211_channel *
-ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
- enum nl80211_channel_type *type)
+static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct ieee80211_chanctx_conf *chanctx_conf;
- struct ieee80211_channel *chan = NULL;
+ int ret = -ENODATA;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf) {
- *type = chanctx_conf->channel_type;
- chan = chanctx_conf->channel;
+ *chandef = chanctx_conf->def;
+ ret = 0;
}
rcu_read_unlock();
- return chan;
+ return ret;
}
#ifdef CONFIG_PM
@@ -3133,6 +3204,7 @@ struct cfg80211_ops mac80211_config_ops = {
.disassoc = ieee80211_disassoc,
.join_ibss = ieee80211_join_ibss,
.leave_ibss = ieee80211_leave_ibss,
+ .set_mcast_rate = ieee80211_set_mcast_rate,
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index f84b86028a9c..53f03120db55 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -8,93 +8,47 @@
#include "ieee80211_i.h"
#include "driver-ops.h"
-static bool
-ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
- enum nl80211_channel_type chantype2,
- enum nl80211_channel_type *compat)
+static void ieee80211_change_chandef(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ const struct cfg80211_chan_def *chandef)
{
- /*
- * 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 (chantype2 == NL80211_CHAN_NO_HT)
- break;
- if (compat)
- *compat = chantype2;
- break;
- case NL80211_CHAN_HT40PLUS:
- case NL80211_CHAN_HT40MINUS:
- /* allow smaller bandwidth and same */
- if (chantype2 == NL80211_CHAN_NO_HT)
- break;
- if (chantype2 == NL80211_CHAN_HT20)
- break;
- if (chantype2 == chantype1)
- break;
- return false;
- }
-
- return true;
-}
-
-static void ieee80211_change_chantype(struct ieee80211_local *local,
- struct ieee80211_chanctx *ctx,
- enum nl80211_channel_type chantype)
-{
- if (chantype == ctx->conf.channel_type)
+ if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
return;
- ctx->conf.channel_type = chantype;
- drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
+ WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
+
+ ctx->conf.def = *chandef;
+ drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
if (!local->use_chanctx) {
- local->_oper_channel_type = chantype;
+ local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
ieee80211_hw_config(local, 0);
}
}
static struct ieee80211_chanctx *
ieee80211_find_chanctx(struct ieee80211_local *local,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
enum ieee80211_chanctx_mode mode)
{
struct ieee80211_chanctx *ctx;
- enum nl80211_channel_type compat_type;
lockdep_assert_held(&local->chanctx_mtx);
if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
return NULL;
- if (WARN_ON(!channel))
- return NULL;
list_for_each_entry(ctx, &local->chanctx_list, list) {
- compat_type = ctx->conf.channel_type;
+ const struct cfg80211_chan_def *compat;
if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
continue;
- if (ctx->conf.channel != channel)
- continue;
- if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type,
- channel_type,
- &compat_type))
+
+ compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
+ if (!compat)
continue;
- ieee80211_change_chantype(local, ctx, compat_type);
+ ieee80211_change_chandef(local, ctx, compat);
return ctx;
}
@@ -104,8 +58,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
static struct ieee80211_chanctx *
ieee80211_new_chanctx(struct ieee80211_local *local,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
enum ieee80211_chanctx_mode mode)
{
struct ieee80211_chanctx *ctx;
@@ -117,15 +70,15 @@ ieee80211_new_chanctx(struct ieee80211_local *local,
if (!ctx)
return ERR_PTR(-ENOMEM);
- ctx->conf.channel = channel;
- ctx->conf.channel_type = channel_type;
+ ctx->conf.def = *chandef;
ctx->conf.rx_chains_static = 1;
ctx->conf.rx_chains_dynamic = 1;
ctx->mode = mode;
if (!local->use_chanctx) {
- local->_oper_channel_type = channel_type;
- local->_oper_channel = channel;
+ local->_oper_channel_type =
+ cfg80211_get_chandef_type(chandef);
+ local->_oper_channel = chandef->chan;
ieee80211_hw_config(local, 0);
} else {
err = drv_add_chanctx(local, ctx);
@@ -173,44 +126,42 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
ctx->refcount++;
+ ieee80211_recalc_txpower(sdata);
+
return 0;
}
-static enum nl80211_channel_type
-ieee80211_calc_chantype(struct ieee80211_local *local,
- struct ieee80211_chanctx *ctx)
+static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
{
struct ieee80211_chanctx_conf *conf = &ctx->conf;
struct ieee80211_sub_if_data *sdata;
- enum nl80211_channel_type result = NL80211_CHAN_NO_HT;
+ const struct cfg80211_chan_def *compat = NULL;
lockdep_assert_held(&local->chanctx_mtx);
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+
if (!ieee80211_sdata_running(sdata))
continue;
if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
continue;
- WARN_ON_ONCE(!ieee80211_channel_types_are_compatible(
- sdata->vif.bss_conf.channel_type,
- result, &result));
+ if (!compat)
+ compat = &sdata->vif.bss_conf.chandef;
+
+ compat = cfg80211_chandef_compatible(
+ &sdata->vif.bss_conf.chandef, compat);
+ if (!compat)
+ break;
}
rcu_read_unlock();
- return result;
-}
-
-static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
- struct ieee80211_chanctx *ctx)
-{
- enum nl80211_channel_type chantype;
-
- lockdep_assert_held(&local->chanctx_mtx);
+ if (WARN_ON_ONCE(!compat))
+ return;
- chantype = ieee80211_calc_chantype(local, ctx);
- ieee80211_change_chantype(local, ctx, chantype);
+ ieee80211_change_chandef(local, ctx, compat);
}
static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
@@ -335,8 +286,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
}
int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
enum ieee80211_chanctx_mode mode)
{
struct ieee80211_local *local = sdata->local;
@@ -348,15 +298,15 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
mutex_lock(&local->chanctx_mtx);
__ieee80211_vif_release_channel(sdata);
- ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
+ ctx = ieee80211_find_chanctx(local, chandef, mode);
if (!ctx)
- ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
+ ctx = ieee80211_new_chanctx(local, chandef, mode);
if (IS_ERR(ctx)) {
ret = PTR_ERR(ctx);
goto out;
}
- sdata->vif.bss_conf.channel_type = channel_type;
+ sdata->vif.bss_conf.chandef = *chandef;
ret = ieee80211_assign_vif_chanctx(sdata, ctx);
if (ret) {
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 090d08ff22c4..2d4235497f1b 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -116,7 +116,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_key *key = file->private_data;
- char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf;
+ char buf[14*IEEE80211_NUM_TIDS+1], *p = buf;
int i, len;
const u8 *rpn;
@@ -126,7 +126,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
len = scnprintf(buf, sizeof(buf), "\n");
break;
case WLAN_CIPHER_SUITE_TKIP:
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
p += scnprintf(p, sizeof(buf)+buf-p,
"%08x %04x\n",
key->u.tkip.rx[i].iv32,
@@ -134,7 +134,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
len = p - buf;
break;
case WLAN_CIPHER_SUITE_CCMP:
- for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
rpn = key->u.ccmp.rx_pn[i];
p += scnprintf(p, sizeof(buf)+buf-p,
"%02x%02x%02x%02x%02x%02x\n",
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 3393ad5b8ab1..cbde5cc49a40 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/if.h>
+#include <linux/if_ether.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
@@ -167,7 +168,29 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
IEEE80211_IF_FILE(flags, flags, HEX);
IEEE80211_IF_FILE(state, state, LHEX);
-IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC);
+IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
+IEEE80211_IF_FILE(ap_power_level, ap_power_level, DEC);
+IEEE80211_IF_FILE(user_power_level, user_power_level, DEC);
+
+static ssize_t
+ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata,
+ char *buf, int buflen)
+{
+ int len;
+
+ len = scnprintf(buf, buflen, "AC queues: VO:%d VI:%d BE:%d BK:%d\n",
+ sdata->vif.hw_queue[IEEE80211_AC_VO],
+ sdata->vif.hw_queue[IEEE80211_AC_VI],
+ sdata->vif.hw_queue[IEEE80211_AC_BE],
+ sdata->vif.hw_queue[IEEE80211_AC_BK]);
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ len += scnprintf(buf + len, buflen - len, "cab queue: %d\n",
+ sdata->vif.cab_queue);
+
+ return len;
+}
+__IEEE80211_IF_FILE(hw_queues, NULL);
/* STA attributes */
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
@@ -245,27 +268,6 @@ static ssize_t ieee80211_if_fmt_tkip_mic_test(
return -EOPNOTSUPP;
}
-static int hwaddr_aton(const char *txt, u8 *addr)
-{
- int i;
-
- for (i = 0; i < ETH_ALEN; i++) {
- int a, b;
-
- a = hex_to_bin(*txt++);
- if (a < 0)
- return -1;
- b = hex_to_bin(*txt++);
- if (b < 0)
- return -1;
- *addr++ = (a << 4) | b;
- if (i < 5 && *txt++ != ':')
- return -1;
- }
-
- return 0;
-}
-
static ssize_t ieee80211_if_parse_tkip_mic_test(
struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
{
@@ -275,13 +277,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
struct ieee80211_hdr *hdr;
__le16 fc;
- /*
- * Assume colon-delimited MAC address with possible white space
- * following.
- */
- if (buflen < 3 * ETH_ALEN - 1)
- return -EINVAL;
- if (hwaddr_aton(buf, addr) < 0)
+ if (!mac_pton(buf, addr))
return -EINVAL;
if (!ieee80211_sdata_running(sdata))
@@ -307,13 +303,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
case NL80211_IFTYPE_STATION:
fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
/* BSSID SA DA */
- if (sdata->vif.bss_conf.bssid == NULL) {
+ mutex_lock(&sdata->u.mgd.mtx);
+ if (!sdata->u.mgd.associated) {
+ mutex_unlock(&sdata->u.mgd.mtx);
dev_kfree_skb(skb);
return -ENOTCONN;
}
- memcpy(hdr->addr1, sdata->vif.bss_conf.bssid, ETH_ALEN);
+ memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN);
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr->addr3, addr, ETH_ALEN);
+ mutex_unlock(&sdata->u.mgd.mtx);
break;
default:
dev_kfree_skb(skb);
@@ -443,7 +442,7 @@ static ssize_t ieee80211_if_parse_tsf(
}
ret = kstrtoull(buf, 10, &tsf);
if (ret < 0)
- return -EINVAL;
+ return ret;
if (tsf_is_delta)
tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf;
if (local->ops->set_tsf) {
@@ -531,6 +530,7 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
+ DEBUGFS_ADD(hw_queues);
}
static void add_sta_files(struct ieee80211_sub_if_data *sdata)
@@ -631,7 +631,9 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(flags);
DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
+ DEBUGFS_ADD(txpower);
+ DEBUGFS_ADD(user_power_level);
+ DEBUGFS_ADD(ap_power_level);
if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
add_common_files(sdata);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 5ccec2c1e9f6..89281d24b094 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -14,6 +14,7 @@
#include "debugfs.h"
#include "debugfs_sta.h"
#include "sta_info.h"
+#include "driver-ops.h"
/* sta attributtes */
@@ -131,10 +132,10 @@ STA_OPS(connected_time);
static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- char buf[15*NUM_RX_DATA_QUEUES], *p = buf;
+ char buf[15*IEEE80211_NUM_TIDS], *p = buf;
int i;
struct sta_info *sta = file->private_data;
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%x ",
le16_to_cpu(sta->last_seq_ctrl[i]));
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
@@ -145,7 +146,7 @@ STA_OPS(last_seq_ctrl);
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- char buf[71 + STA_TID_NUM * 40], *p = buf;
+ char buf[71 + IEEE80211_NUM_TIDS * 40], *p = buf;
int i;
struct sta_info *sta = file->private_data;
struct tid_ampdu_rx *tid_rx;
@@ -158,7 +159,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf) + buf - p,
"TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n");
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]);
tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]);
@@ -220,7 +221,7 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu
tid = simple_strtoul(buf, NULL, 0);
- if (tid >= STA_TID_NUM)
+ if (tid >= IEEE80211_NUM_TIDS)
return -EINVAL;
if (tx) {
@@ -334,6 +335,8 @@ STA_OPS(ht_capa);
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations;
u8 mac[3*ETH_ALEN];
@@ -379,10 +382,16 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed);
DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
+
+ drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
}
void ieee80211_sta_debugfs_remove(struct sta_info *sta)
{
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
debugfs_remove_recursive(sta->debugfs.dir);
sta->debugfs.dir = NULL;
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 77407b31e1ff..c6560cc7a9d6 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -490,6 +490,38 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+#ifdef CONFIG_MAC80211_DEBUGFS
+static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct dentry *dir)
+{
+ might_sleep();
+
+ sdata = get_bss_sdata(sdata);
+ check_sdata_in_driver(sdata);
+
+ if (local->ops->sta_add_debugfs)
+ local->ops->sta_add_debugfs(&local->hw, &sdata->vif,
+ sta, dir);
+}
+
+static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct dentry *dir)
+{
+ might_sleep();
+
+ sdata = get_bss_sdata(sdata);
+ check_sdata_in_driver(sdata);
+
+ if (local->ops->sta_remove_debugfs)
+ local->ops->sta_remove_debugfs(&local->hw, &sdata->vif,
+ sta, dir);
+}
+#endif
+
static inline __must_check
int drv_sta_state(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -704,17 +736,17 @@ static inline int drv_get_antenna(struct ieee80211_local *local,
}
static inline int drv_remain_on_channel(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *chan,
- enum nl80211_channel_type chantype,
unsigned int duration)
{
int ret;
might_sleep();
- trace_drv_remain_on_channel(local, chan, chantype, duration);
- ret = local->ops->remain_on_channel(&local->hw, chan, chantype,
- duration);
+ trace_drv_remain_on_channel(local, sdata, chan, duration);
+ ret = local->ops->remain_on_channel(&local->hw, &sdata->vif,
+ chan, duration);
trace_drv_return_int(local, ret);
return ret;
@@ -936,4 +968,39 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline int drv_start_ap(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ int ret = 0;
+
+ check_sdata_in_driver(sdata);
+
+ trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);
+ if (local->ops->start_ap)
+ ret = local->ops->start_ap(&local->hw, &sdata->vif);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline void drv_stop_ap(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ check_sdata_in_driver(sdata);
+
+ trace_drv_stop_ap(local, sdata);
+ if (local->ops->stop_ap)
+ local->ops->stop_ap(&local->hw, &sdata->vif);
+ trace_drv_return_void(local);
+}
+
+static inline void drv_restart_complete(struct ieee80211_local *local)
+{
+ might_sleep();
+
+ trace_drv_restart_complete(local);
+ if (local->ops->restart_complete)
+ local->ops->restart_complete(&local->hw);
+ trace_drv_return_void(local);
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 4b4538d63925..a71d891794a4 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -185,7 +185,7 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
cancel_work_sync(&sta->ampdu_mlme.work);
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
__ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx);
__ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_LEAVE_QBSS, tx);
@@ -209,7 +209,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
return;
mutex_lock(&sta->ampdu_mlme.mtx);
- for (tid = 0; tid < STA_TID_NUM; tid++) {
+ for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired))
___ieee80211_stop_rx_ba_session(
sta, tid, WLAN_BACK_RECIPIENT,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 67774b053535..fa862b24a7e0 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -51,7 +51,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *bss;
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
- enum nl80211_channel_type channel_type;
+ struct cfg80211_chan_def chandef;
lockdep_assert_held(&ifibss->mtx);
@@ -79,12 +79,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- channel_type = ifibss->channel_type;
- if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type))
- channel_type = NL80211_CHAN_HT20;
+ cfg80211_chandef_create(&chandef, chan, ifibss->channel_type);
+ if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+ chandef.width = NL80211_CHAN_WIDTH_20;
+ chandef.center_freq1 = chan->center_freq;
+ }
ieee80211_vif_release_channel(sdata);
- if (ieee80211_vif_use_channel(sdata, chan, channel_type,
+ if (ieee80211_vif_use_channel(sdata, &chandef,
ifibss->fixed_channel ?
IEEE80211_CHANCTX_SHARED :
IEEE80211_CHANCTX_EXCLUSIVE)) {
@@ -158,7 +160,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
ifibss->ie, ifibss->ie_len);
/* add HT capability and information IEs */
- if (channel_type && sband->ht_cap.ht_supported) {
+ if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ sband->ht_cap.ht_supported) {
pos = skb_put(skb, 4 +
sizeof(struct ieee80211_ht_cap) +
sizeof(struct ieee80211_ht_operation));
@@ -170,7 +173,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
* keep them at 0
*/
pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
- chan, channel_type, 0);
+ &chandef, 0);
}
if (local->hw.queues >= IEEE80211_NUM_ACS) {
@@ -326,7 +329,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (WARN_ON_ONCE(!chanctx_conf))
return NULL;
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
rcu_read_unlock();
sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
@@ -374,11 +377,13 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
- if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
- return;
ibss_dbg(sdata,
"RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n",
mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
+
+ if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
+ return;
+
sta_info_destroy_addr(sdata, mgmt->sa);
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
rcu_read_unlock();
@@ -473,9 +478,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
/* we both use HT */
struct ieee80211_sta_ht_cap sta_ht_cap_new;
- enum nl80211_channel_type channel_type =
- ieee80211_ht_oper_to_channel_type(
- elems->ht_operation);
+ struct cfg80211_chan_def chandef;
+
+ ieee80211_ht_oper_to_chandef(channel,
+ elems->ht_operation,
+ &chandef);
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
@@ -485,9 +492,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* fall back to HT20 if we don't use or use
* the other extension channel
*/
- if (!(channel_type == NL80211_CHAN_HT40MINUS ||
- channel_type == NL80211_CHAN_HT40PLUS) ||
- channel_type != sdata->u.ibss.channel_type)
+ if (chandef.width != NL80211_CHAN_WIDTH_40 ||
+ cfg80211_get_chandef_type(&chandef) !=
+ sdata->u.ibss.channel_type)
sta_ht_cap_new.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -543,30 +550,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
goto put_bss;
- if (rx_status->flag & RX_FLAG_MACTIME_MPDU) {
- /*
- * For correct IBSS merging we need mactime; since mactime is
- * defined as the time the first data symbol of the frame hits
- * the PHY, and the timestamp of the beacon is defined as "the
- * time that the data symbol containing the first bit of the
- * timestamp is transmitted to the PHY plus the transmitting
- * STA's delays through its local PHY from the MAC-PHY
- * interface to its interface with the WM" (802.11 11.1.2)
- * - equals the time this bit arrives at the receiver - we have
- * to take into account the offset between the two.
- *
- * E.g. at 1 MBit that means mactime is 192 usec earlier
- * (=24 bytes * 8 usecs/byte) than the beacon timestamp.
- */
- int rate;
-
- if (rx_status->flag & RX_FLAG_HT)
- rate = 65; /* TODO: HT rates */
- else
- rate = local->hw.wiphy->bands[band]->
- bitrates[rx_status->rate_idx].bitrate;
-
- rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
+ if (ieee80211_have_rx_timestamp(rx_status)) {
+ /* time when timestamp field was received */
+ rx_timestamp =
+ ieee80211_calculate_rx_timestamp(local, rx_status,
+ len + FCS_LEN, 24);
} else {
/*
* second best option: get current TSF
@@ -630,7 +618,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
return;
}
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
rcu_read_unlock();
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -1095,8 +1083,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
- sdata->u.ibss.channel = params->channel;
- sdata->u.ibss.channel_type = params->channel_type;
+ sdata->u.ibss.channel = params->chandef.chan;
+ sdata->u.ibss.channel_type =
+ cfg80211_get_chandef_type(&params->chandef);
sdata->u.ibss.fixed_channel = params->channel_fixed;
if (params->ie) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 32e47853f329..5c0d5a6946c1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -56,6 +56,9 @@ struct ieee80211_local;
#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
+/* power level hasn't been configured (or set to automatic) */
+#define IEEE80211_UNSET_POWER_LEVEL INT_MIN
+
/*
* Some APs experience problems when working with U-APSD. Decrease the
* probability of that happening by using legacy mode for all ACs but VO.
@@ -345,7 +348,6 @@ struct ieee80211_roc_work {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
- enum nl80211_channel_type chan_type;
bool started, abort, hw_begun, notified;
@@ -353,7 +355,7 @@ struct ieee80211_roc_work {
u32 duration, req_duration;
struct sk_buff *frame;
- u64 mgmt_tx_cookie;
+ u64 cookie, mgmt_tx_cookie;
};
/* flags used in struct ieee80211_if_managed.flags */
@@ -361,7 +363,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_BEACON_POLL = BIT(0),
IEEE80211_STA_CONNECTION_POLL = BIT(1),
IEEE80211_STA_CONTROL_PORT = BIT(2),
- IEEE80211_STA_DISABLE_11N = BIT(4),
+ IEEE80211_STA_DISABLE_HT = BIT(4),
IEEE80211_STA_CSA_RECEIVED = BIT(5),
IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
@@ -470,6 +472,8 @@ struct ieee80211_if_managed {
u8 use_4addr;
+ u8 p2p_noa_index;
+
/* Signal strength from the last Beacon frame in the current BSS. */
int last_beacon_signal;
@@ -743,6 +747,9 @@ struct ieee80211_sub_if_data {
u8 needed_rx_chains;
enum ieee80211_smps_mode smps_mode;
+ int user_power_level; /* in dBm */
+ int ap_power_level; /* in dBm */
+
/*
* AP this belongs to: self in AP mode and
* corresponding AP in VLAN mode, NULL for
@@ -792,7 +799,7 @@ ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!WARN_ON(!chanctx_conf))
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
rcu_read_unlock();
return band;
@@ -1040,7 +1047,6 @@ struct ieee80211_local {
/* Temporary remain-on-channel for off-channel operations */
struct ieee80211_channel *tmp_channel;
- enum nl80211_channel_type tmp_channel_type;
/* channel contexts */
struct list_head chanctx_list;
@@ -1117,8 +1123,7 @@ struct ieee80211_local {
int dynamic_ps_user_timeout;
bool disable_dynamic_ps;
- int user_power_level; /* in dBm */
- int ap_power_level; /* in dBm */
+ int user_power_level; /* in dBm, for all interfaces */
enum ieee80211_smps_mode smps_mode;
@@ -1137,6 +1142,7 @@ struct ieee80211_local {
struct list_head roc_list;
struct work_struct hw_roc_start, hw_roc_done;
unsigned long hw_roc_start_time;
+ u64 roc_cookie_counter;
struct idr ack_status_frames;
spinlock_t ack_status_lock;
@@ -1150,8 +1156,7 @@ struct ieee80211_local {
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
- struct ieee80211_channel *monitor_channel;
- enum nl80211_channel_type monitor_channel_type;
+ struct cfg80211_chan_def monitor_chandef;
};
static inline struct ieee80211_sub_if_data *
@@ -1251,7 +1256,18 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
is_broadcast_ether_addr(raddr);
}
+static inline bool
+ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status)
+{
+ WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START &&
+ status->flag & RX_FLAG_MACTIME_END);
+ return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END);
+}
+u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status,
+ unsigned int mpdu_len,
+ unsigned int mpdu_offset);
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
@@ -1365,6 +1381,9 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up);
void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
+bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{
return test_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -1496,7 +1515,7 @@ static inline void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
}
__ieee80211_tx_skb_tid_band(sdata, skb, tid,
- chanctx_conf->channel->band);
+ chanctx_conf->def.chan->band);
rcu_read_unlock();
}
@@ -1585,8 +1604,7 @@ size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 cap);
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
u16 prot_mode);
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap);
@@ -1598,13 +1616,13 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band);
/* channel management */
-enum nl80211_channel_type
-ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper);
+void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
+ struct ieee80211_ht_operation *ht_oper,
+ struct cfg80211_chan_def *chandef);
int __must_check
ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
enum ieee80211_chanctx_mode mode);
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index bc3e3e1db093..5331662489f7 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -42,6 +42,41 @@
* by either the RTNL, the iflist_mtx or RCU.
*/
+bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int power;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ return false;
+ }
+
+ power = chanctx_conf->def.chan->max_power;
+ rcu_read_unlock();
+
+ if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->user_power_level);
+
+ if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->ap_power_level);
+
+ if (power != sdata->vif.bss_conf.txpower) {
+ sdata->vif.bss_conf.txpower = power;
+ ieee80211_hw_config(sdata->local, 0);
+ return true;
+ }
+
+ return false;
+}
+
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+{
+ if (__ieee80211_recalc_txpower(sdata))
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
+}
static u32 ieee80211_idle_off(struct ieee80211_local *local,
const char *reason)
@@ -380,8 +415,7 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
goto out_unlock;
}
- ret = ieee80211_vif_use_channel(sdata, local->monitor_channel,
- local->monitor_channel_type,
+ ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,
IEEE80211_CHANCTX_EXCLUSIVE);
if (ret) {
drv_remove_interface(local, sdata);
@@ -744,31 +778,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
struct ieee80211_sub_if_data *vlan, *tmpsdata;
- struct beacon_data *old_beacon =
- rtnl_dereference(sdata->u.ap.beacon);
- struct probe_resp *old_probe_resp =
- rtnl_dereference(sdata->u.ap.probe_resp);
-
- /* sdata_running will return false, so this will disable */
- ieee80211_bss_info_change_notify(sdata,
- BSS_CHANGED_BEACON_ENABLED);
-
- /* remove beacon and probe response */
- RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
- RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
- synchronize_rcu();
- kfree(old_beacon);
- kfree(old_probe_resp);
/* down all dependent devices, that is VLANs */
list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
u.vlan.list)
dev_close(vlan->dev);
WARN_ON(!list_empty(&sdata->u.ap.vlans));
-
- /* free all potentially still buffered bcast frames */
- local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
- skb_queue_purge(&sdata->u.ap.ps.bc_buf);
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ieee80211_mgd_stop(sdata);
}
@@ -1529,6 +1544,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ieee80211_set_default_queues(sdata);
+ sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ sdata->user_power_level = local->user_power_level;
+
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index d27e61aaa71b..619c5d697999 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -339,7 +339,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = TKIP_IV_LEN;
key->conf.icv_len = TKIP_ICV_LEN;
if (seq) {
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
key->u.tkip.rx[i].iv32 =
get_unaligned_le32(&seq[2]);
key->u.tkip.rx[i].iv16 =
@@ -352,7 +352,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = CCMP_HDR_LEN;
key->conf.icv_len = CCMP_MIC_LEN;
if (seq) {
- for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
for (j = 0; j < CCMP_PN_LEN; j++)
key->u.ccmp.rx_pn[i][j] =
seq[CCMP_PN_LEN - j - 1];
@@ -372,8 +372,9 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = 0;
key->conf.icv_len = sizeof(struct ieee80211_mmie);
if (seq)
- for (j = 0; j < 6; j++)
- key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1];
+ for (j = 0; j < CMAC_PN_LEN; j++)
+ key->u.aes_cmac.rx_pn[j] =
+ seq[CMAC_PN_LEN - j - 1];
/*
* Initialize AES key state here as an optimization so that
* it does not need to be initialized for every packet.
@@ -654,16 +655,16 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_TKIP:
- if (WARN_ON(tid < 0 || tid >= NUM_RX_DATA_QUEUES))
+ if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS))
return;
seq->tkip.iv32 = key->u.tkip.rx[tid].iv32;
seq->tkip.iv16 = key->u.tkip.rx[tid].iv16;
break;
case WLAN_CIPHER_SUITE_CCMP:
- if (WARN_ON(tid < -1 || tid >= NUM_RX_DATA_QUEUES))
+ if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
return;
if (tid < 0)
- pn = key->u.ccmp.rx_pn[NUM_RX_DATA_QUEUES];
+ pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
else
pn = key->u.ccmp.rx_pn[tid];
memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 7d4e31f037d7..7cff0d3a519c 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -30,8 +30,6 @@
#define TKIP_ICV_LEN 4
#define CMAC_PN_LEN 6
-#define NUM_RX_DATA_QUEUES 16
-
struct ieee80211_local;
struct ieee80211_sub_if_data;
struct sta_info;
@@ -82,17 +80,17 @@ struct ieee80211_key {
struct tkip_ctx tx;
/* last received RSC */
- struct tkip_ctx rx[NUM_RX_DATA_QUEUES];
+ struct tkip_ctx rx[IEEE80211_NUM_TIDS];
} tkip;
struct {
atomic64_t tx_pn;
/*
* Last received packet number. The first
- * NUM_RX_DATA_QUEUES counters are used with Data
+ * IEEE80211_NUM_TIDS counters are used with Data
* frames and the last counter is used with Robust
* Management frames.
*/
- u8 rx_pn[NUM_RX_DATA_QUEUES + 1][CCMP_PN_LEN];
+ u8 rx_pn[IEEE80211_NUM_TIDS + 1][CCMP_PN_LEN];
struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCCMPReplays */
} ccmp;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index d6e43b08d629..f5e4c1f24bf2 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -95,11 +95,13 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
{
+ struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
u32 changed = 0;
int power;
enum nl80211_channel_type channel_type;
u32 offchannel_flag;
+ bool scanning = false;
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (local->scan_channel) {
@@ -113,7 +115,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
channel_type = NL80211_CHAN_NO_HT;
} else if (local->tmp_channel) {
chan = local->tmp_channel;
- channel_type = local->tmp_channel_type;
+ channel_type = NL80211_CHAN_NO_HT;
} else {
chan = local->_oper_channel;
channel_type = local->_oper_channel_type;
@@ -146,16 +148,18 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
changed |= IEEE80211_CONF_CHANGE_SMPS;
}
- if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
- test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
- test_bit(SCAN_HW_SCANNING, &local->scanning) ||
- !local->ap_power_level)
- power = chan->max_power;
- else
- power = min(chan->max_power, local->ap_power_level);
+ scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
+ test_bit(SCAN_HW_SCANNING, &local->scanning);
+ power = chan->max_power;
- if (local->user_power_level >= 0)
- power = min(power, local->user_power_level);
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!rcu_access_pointer(sdata->vif.chanctx_conf))
+ continue;
+ power = min(power, sdata->vif.bss_conf.txpower);
+ }
+ rcu_read_unlock();
if (local->hw.conf.power_level != power) {
changed |= IEEE80211_CONF_CHANGE_POWER;
@@ -600,7 +604,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
NL80211_FEATURE_SAE |
- NL80211_FEATURE_HT_IBSS;
+ NL80211_FEATURE_HT_IBSS |
+ NL80211_FEATURE_VIF_TXPOWER;
if (!ops->hw_scan)
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
@@ -633,7 +638,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_BW;
- local->user_power_level = -1;
+ local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
INIT_LIST_HEAD(&local->interfaces);
@@ -793,10 +798,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->_oper_channel = &sband->channels[0];
local->hw.conf.channel_type = NL80211_CHAN_NO_HT;
}
- if (!local->monitor_channel) {
- local->monitor_channel = &sband->channels[0];
- local->monitor_channel_type = NL80211_CHAN_NO_HT;
- }
+ cfg80211_chandef_create(&local->monitor_chandef,
+ &sband->channels[0],
+ NL80211_CHAN_NO_HT);
channels += sband->n_channels;
if (max_bitrates < sband->n_bitrates)
@@ -879,10 +883,22 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (supp_ht)
local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap);
- if (supp_vht)
+ if (supp_vht) {
local->scan_ies_len +=
2 + sizeof(struct ieee80211_vht_cap);
+ /*
+ * (for now at least), drivers wanting to use VHT must
+ * support channel contexts, as they contain all the
+ * necessary VHT information and the global hw config
+ * doesn't (yet)
+ */
+ if (WARN_ON(!local->use_chanctx)) {
+ result = -EINVAL;
+ goto fail_wiphy_register;
+ }
+ }
+
if (!local->ops->hw_scan) {
/* For hw_scan, driver needs to set these up. */
local->hw.wiphy->max_scan_ssids = 4;
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index a350cab4b339..1bf03f9ff3ba 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -76,7 +76,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
u32 basic_rates = 0;
- enum nl80211_channel_type sta_channel_type = NL80211_CHAN_NO_HT;
+ struct cfg80211_chan_def sta_chan_def;
/*
* As support for each feature is added, check for matching
@@ -103,17 +103,11 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.bss_conf.basic_rates != basic_rates)
goto mismatch;
- if (ie->ht_operation)
- sta_channel_type =
- ieee80211_ht_oper_to_channel_type(ie->ht_operation);
-
- /* Disallow HT40+/- mismatch */
- if (ie->ht_operation &&
- (sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40MINUS ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40PLUS) &&
- (sta_channel_type == NL80211_CHAN_HT40MINUS ||
- sta_channel_type == NL80211_CHAN_HT40PLUS) &&
- sdata->vif.bss_conf.channel_type != sta_channel_type)
+ ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
+ ie->ht_operation, &sta_chan_def);
+
+ if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
+ &sta_chan_def))
goto mismatch;
return true;
@@ -129,7 +123,7 @@ mismatch:
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie)
{
return (ie->mesh_config->meshconf_cap &
- MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;
+ IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;
}
/**
@@ -269,11 +263,11 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
neighbors = (neighbors > 15) ? 15 : neighbors;
*pos++ = neighbors << 1;
/* Mesh capability */
- *pos = MESHCONF_CAPAB_FORWARDING;
+ *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
*pos |= ifmsh->accepting_plinks ?
- MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
+ IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ |= ifmsh->adjusting_tbtt ?
- MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
+ IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
*pos++ = 0x00;
return 0;
@@ -368,7 +362,7 @@ int mesh_add_ds_params_ie(struct sk_buff *skb,
rcu_read_unlock();
return -EINVAL;
}
- chan = chanctx_conf->channel;
+ chan = chanctx_conf->def.chan;
rcu_read_unlock();
sband = local->hw.wiphy->bands[chan->band];
@@ -392,7 +386,7 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb,
sband = local->hw.wiphy->bands[band];
if (!sband->ht_cap.ht_supported ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
@@ -411,7 +405,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type =
- sdata->vif.bss_conf.channel_type;
+ cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef);
struct ieee80211_supported_band *sband;
struct ieee80211_sta_ht_cap *ht_cap;
u8 *pos;
@@ -422,7 +416,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
rcu_read_unlock();
return -EINVAL;
}
- channel = chanctx_conf->channel;
+ channel = chanctx_conf->def.chan;
rcu_read_unlock();
sband = local->hw.wiphy->bands[channel->band];
@@ -435,7 +429,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
return -ENOMEM;
pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
- ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type,
+ ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef,
sdata->vif.bss_conf.ht_operation_mode);
return 0;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 9285f3f67e66..7c9215fb2ac8 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -19,20 +19,6 @@
/* Data structures */
/**
- * enum mesh_config_capab_flags - mesh config IE capability flags
- *
- * @MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish
- * additional mesh peerings with other mesh STAs
- * @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs
- * @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing
- */
-enum mesh_config_capab_flags {
- MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0),
- MESHCONF_CAPAB_FORWARDING = BIT(3),
- MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5),
-};
-
-/**
* enum mesh_path_flags - mac80211 mesh path flags
*
*
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 234fe755968b..ca52dfdd5375 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -19,12 +19,6 @@
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000))
-#define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries)
-#define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout)
-#define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout)
-#define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout)
-#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
-
/* We only need a valid sta if user configured a minimum rssi_threshold. */
#define rssi_threshold_check(sta, sdata) \
(sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\
@@ -117,7 +111,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
u16 ht_opmode;
bool non_ht_sta = false, ht20_sta = false;
- if (sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
+ if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
rcu_read_lock();
@@ -126,14 +120,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
sta->plink_state != NL80211_PLINK_ESTAB)
continue;
- switch (sta->ch_type) {
- case NL80211_CHAN_NO_HT:
+ switch (sta->ch_width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
mpl_dbg(sdata,
"mesh_plink %pM: nonHT sta (%pM) is present\n",
sdata->vif.addr, sta->sta.addr);
non_ht_sta = true;
goto out;
- case NL80211_CHAN_HT20:
+ case NL80211_CHAN_WIDTH_20:
mpl_dbg(sdata,
"mesh_plink %pM: HT20 sta (%pM) is present\n",
sdata->vif.addr, sta->sta.addr);
@@ -148,7 +142,7 @@ out:
if (non_ht_sta)
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED;
else if (ht20_sta &&
- sdata->vif.bss_conf.channel_type > NL80211_CHAN_HT20)
+ sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20)
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ;
else
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
@@ -378,7 +372,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
sta->sta.supp_rates[band] = rates;
if (elems->ht_cap_elem &&
- sdata->vif.bss_conf.channel_type != NL80211_CHAN_NO_HT)
+ sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
&sta->sta.ht_cap);
@@ -386,12 +380,15 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap));
if (elems->ht_operation) {
+ struct cfg80211_chan_def chandef;
+
if (!(elems->ht_operation->ht_param &
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
sta->sta.ht_cap.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- sta->ch_type =
- ieee80211_ht_oper_to_channel_type(elems->ht_operation);
+ ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
+ elems->ht_operation, &chandef);
+ sta->ch_width = chandef.width;
}
rate_control_rate_init(sta);
@@ -430,6 +427,7 @@ static void mesh_plink_timer(unsigned long data)
struct sta_info *sta;
__le16 llid, plid, reason;
struct ieee80211_sub_if_data *sdata;
+ struct mesh_config *mshcfg;
/*
* This STA is valid because sta_info_destroy() will
@@ -456,12 +454,13 @@ static void mesh_plink_timer(unsigned long data)
llid = sta->llid;
plid = sta->plid;
sdata = sta->sdata;
+ mshcfg = &sdata->u.mesh.mshcfg;
switch (sta->plink_state) {
case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_OPN_SNT:
/* retry timer */
- if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
+ if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) {
u32 rand;
mpl_dbg(sta->sdata,
"Mesh plink for %pM (retry, timeout): %d %d\n",
@@ -484,7 +483,7 @@ static void mesh_plink_timer(unsigned long data)
if (!reason)
reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT);
sta->plink_state = NL80211_PLINK_HOLDING;
- mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+ mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, llid, plid, reason);
@@ -543,7 +542,7 @@ int mesh_plink_open(struct sta_info *sta)
return -EBUSY;
}
sta->plink_state = NL80211_PLINK_OPN_SNT;
- mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+ mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
spin_unlock_bh(&sta->lock);
mpl_dbg(sdata,
"Mesh plink: starting establishment with %pM\n",
@@ -570,6 +569,7 @@ void mesh_plink_block(struct sta_info *sta)
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt,
size_t len, struct ieee80211_rx_status *rx_status)
{
+ struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
struct ieee802_11_elems elems;
struct sta_info *sta;
enum plink_event event;
@@ -777,7 +777,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plid = plid;
get_random_bytes(&llid, 2);
sta->llid = llid;
- mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+ mesh_plink_timer_set(sta,
+ mshcfg->dot11MeshRetryTimeout);
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_OPEN,
@@ -803,7 +804,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -825,7 +826,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
case CNF_ACPT:
sta->plink_state = NL80211_PLINK_CNF_RCVD;
if (!mod_plink_timer(sta,
- dot11MeshConfirmTimeout(sdata)))
+ mshcfg->dot11MeshConfirmTimeout))
sta->ignore_plink_timer = true;
spin_unlock_bh(&sta->lock);
@@ -847,7 +848,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -888,7 +889,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -923,7 +924,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
changed |= __mesh_plink_deactivate(sta);
sta->plink_state = NL80211_PLINK_HOLDING;
llid = sta->llid;
- mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+ mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
spin_unlock_bh(&sta->lock);
changed |= mesh_set_ht_prot_mode(sdata);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
index 407c8705e10d..0f40086cce18 100644
--- a/net/mac80211/mesh_sync.c
+++ b/net/mac80211/mesh_sync.c
@@ -43,7 +43,7 @@ struct sync_method {
static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
{
return (ie->mesh_config->meshconf_cap &
- MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
+ IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
}
void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
@@ -116,43 +116,13 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
goto no_sync;
}
- if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) {
- /*
- * The mactime is defined as the time the first data symbol
- * of the frame hits the PHY, and the timestamp of the beacon
- * is defined as "the time that the data symbol containing the
- * first bit of the timestamp is transmitted to the PHY plus
- * the transmitting STA's delays through its local PHY from the
- * MAC-PHY interface to its interface with the WM" (802.11
- * 11.1.2)
- *
- * T_r, in 13.13.2.2.2, is just defined as "the frame reception
- * time" but we unless we interpret that time to be the same
- * time of the beacon timestamp, the offset calculation will be
- * off. Below we adjust t_r to be "the time at which the first
- * symbol of the timestamp element in the beacon is received".
- * This correction depends on the rate.
- *
- * Based on similar code in ibss.c
- */
- int rate;
-
- if (rx_status->flag & RX_FLAG_HT) {
- /* TODO:
- * In principle there could be HT-beacons (Dual Beacon
- * HT Operation options), but for now ignore them and
- * just use the primary (i.e. non-HT) beacons for
- * synchronization.
- * */
- goto no_sync;
- } else
- rate = local->hw.wiphy->bands[rx_status->band]->
- bitrates[rx_status->rate_idx].bitrate;
-
- /* 24 bytes of header * 8 bits/byte *
- * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/
- t_r = rx_status->mactime + (24 * 8 * 10 / rate);
- }
+ if (ieee80211_have_rx_timestamp(rx_status))
+ /* time when timestamp field was received */
+ t_r = ieee80211_calculate_rx_timestamp(local, rx_status,
+ 24 + 12 +
+ elems->total_len +
+ FCS_LEN,
+ 24);
/* Timing offset calculation (see 13.13.2.2.2) */
t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 1d1fdf0791f0..d2a4f78b4b0f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -191,17 +191,19 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
return 0;
}
- chan = chanctx_conf->channel;
+ chan = chanctx_conf->def.chan;
rcu_read_unlock();
sband = local->hw.wiphy->bands[chan->band];
- switch (sdata->vif.bss_conf.channel_type) {
- case NL80211_CHAN_HT40PLUS:
- if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ switch (sdata->vif.bss_conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_40:
+ if (sdata->vif.bss_conf.chandef.chan->center_freq >
+ sdata->vif.bss_conf.chandef.center_freq1 &&
+ chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
disable_40 = true;
- break;
- case NL80211_CHAN_HT40MINUS:
- if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ if (sdata->vif.bss_conf.chandef.chan->center_freq <
+ sdata->vif.bss_conf.chandef.center_freq1 &&
+ chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
disable_40 = true;
break;
default:
@@ -381,7 +383,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
rcu_read_unlock();
return;
}
- chan = chanctx_conf->channel;
+ chan = chanctx_conf->def.chan;
rcu_read_unlock();
sband = local->hw.wiphy->bands[chan->band];
@@ -541,7 +543,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset;
}
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
sband, chan, sdata->smps_mode);
@@ -820,10 +822,10 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
cbss->beacon_interval));
}
-static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel *channel,
- const u8 *country_ie, u8 country_ie_len,
- const u8 *pwr_constr_elem)
+static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *channel,
+ const u8 *country_ie, u8 country_ie_len,
+ const u8 *pwr_constr_elem)
{
struct ieee80211_country_ie_triplet *triplet;
int chan = ieee80211_frequency_to_channel(channel->center_freq);
@@ -832,7 +834,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
/* Invalid IE */
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
- return;
+ return 0;
triplet = (void *)(country_ie + 3);
country_ie_len -= 3;
@@ -873,19 +875,21 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
}
if (!have_chan_pwr)
- return;
+ return 0;
new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
- if (sdata->local->ap_power_level == new_ap_level)
- return;
+ if (sdata->ap_power_level == new_ap_level)
+ return 0;
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);
+ sdata->ap_power_level = new_ap_level;
+ if (__ieee80211_recalc_txpower(sdata))
+ return BSS_CHANGED_TXPOWER;
+ return 0;
}
void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
@@ -1363,6 +1367,22 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
+ if (sdata->vif.p2p) {
+ u8 noa[2];
+ int ret;
+
+ ret = cfg80211_get_p2p_attr(cbss->information_elements,
+ cbss->len_information_elements,
+ IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+ noa, sizeof(noa));
+ if (ret >= 2) {
+ bss_conf->p2p_oppps = noa[1] & 0x80;
+ bss_conf->p2p_ctwindow = noa[1] & 0x7f;
+ bss_info_changed |= BSS_CHANGED_P2P_PS;
+ sdata->u.mgd.p2p_noa_index = noa[0];
+ }
+ }
+
/* just to be sure */
ieee80211_stop_poll(sdata);
@@ -1485,11 +1505,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_ASSOC;
sdata->vif.bss_conf.assoc = false;
+ sdata->vif.bss_conf.p2p_ctwindow = 0;
+ sdata->vif.bss_conf.p2p_oppps = false;
+
/* on the next assoc, re-program HT parameters */
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
- local->ap_power_level = 0;
+ sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
@@ -1507,8 +1530,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
- ieee80211_vif_release_channel(sdata);
-
/* disassociated - set to defaults now */
ieee80211_set_wmm_default(sdata, false);
@@ -1518,6 +1539,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&sdata->u.mgd.chswitch_timer);
sdata->u.mgd.timers_running = 0;
+
+ ifmgd->flags = 0;
+ ieee80211_vif_release_channel(sdata);
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1843,6 +1867,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
+ sdata->u.mgd.flags = 0;
ieee80211_vif_release_channel(sdata);
}
@@ -2085,6 +2110,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
+ sdata->u.mgd.flags = 0;
ieee80211_vif_release_channel(sdata);
}
@@ -2149,7 +2175,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
- if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
@@ -2201,7 +2227,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_QOS;
if (elems.ht_operation && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
cbss->bssid, false);
@@ -2452,11 +2478,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
return;
}
- if (rx_status->freq != chanctx_conf->channel->center_freq) {
+ if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
rcu_read_unlock();
return;
}
- chan = chanctx_conf->channel;
+ chan = chanctx_conf->def.chan;
rcu_read_unlock();
if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
@@ -2592,6 +2618,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
+ if (sdata->vif.p2p) {
+ u8 noa[2];
+ int ret;
+
+ ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,
+ len - baselen,
+ IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+ noa, sizeof(noa));
+ if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) {
+ bss_conf->p2p_oppps = noa[1] & 0x80;
+ bss_conf->p2p_ctwindow = noa[1] & 0x7f;
+ changed |= BSS_CHANGED_P2P_PS;
+ sdata->u.mgd.p2p_noa_index = noa[0];
+ /*
+ * make sure we update all information, the CRC
+ * mechanism doesn't look at P2P attributes.
+ */
+ ifmgd->beacon_crc_valid = false;
+ }
+ }
+
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
@@ -2616,17 +2663,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
bssid, true);
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, chan,
- elems.country_elem,
- elems.country_elem_len,
- elems.pwr_constr_elem);
+ changed |= ieee80211_handle_pwr_constr(sdata, chan,
+ elems.country_elem,
+ elems.country_elem_len,
+ elems.pwr_constr_elem);
ieee80211_bss_info_change_notify(sdata, changed);
}
@@ -3146,6 +3193,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
const u8 *ht_oper_ie;
const struct ieee80211_ht_operation *ht_oper = NULL;
struct ieee80211_supported_band *sband;
+ struct cfg80211_chan_def chandef;
sband = local->hw.wiphy->bands[cbss->channel->band];
@@ -3177,12 +3225,10 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
ht_cfreq, ht_oper->primary_chan,
cbss->channel->band);
ht_oper = NULL;
- } else {
- channel_type = NL80211_CHAN_HT20;
}
}
- if (ht_oper && sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ if (ht_oper) {
/*
* cfg80211 already verified that the channel itself can
* be used, but it didn't check that we can do the right
@@ -3195,19 +3241,26 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
channel_type = NL80211_CHAN_HT20;
- switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
- ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
- else
- channel_type = NL80211_CHAN_HT40PLUS;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
- ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
- else
- channel_type = NL80211_CHAN_HT40MINUS;
- break;
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ switch (ht_oper->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ if (cbss->channel->flags &
+ IEEE80211_CHAN_NO_HT40PLUS)
+ ifmgd->flags |=
+ IEEE80211_STA_DISABLE_40MHZ;
+ else
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ if (cbss->channel->flags &
+ IEEE80211_CHAN_NO_HT40MINUS)
+ ifmgd->flags |=
+ IEEE80211_STA_DISABLE_40MHZ;
+ else
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ }
}
ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
@@ -3220,13 +3273,15 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
sdata->needed_rx_chains = min(chains, local->rx_chains);
} else {
sdata->needed_rx_chains = 1;
+ sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_HT;
}
/* will change later if needed */
sdata->smps_mode = IEEE80211_SMPS_OFF;
ieee80211_vif_release_channel(sdata);
- return ieee80211_vif_use_channel(sdata, cbss->channel, channel_type,
+ cfg80211_chandef_create(&chandef, cbss->channel, channel_type);
+ return ieee80211_vif_use_channel(sdata, &chandef,
IEEE80211_CHANCTX_SHARED);
}
@@ -3488,13 +3543,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* prepare assoc data */
- /*
- * keep only the 40 MHz disable bit set as it might have
- * been set during authentication already, all other bits
- * should be reset for a new connection
- */
- ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ;
-
ifmgd->beacon_crc_valid = false;
/*
@@ -3508,7 +3556,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
netdev_info(sdata->dev,
"disabling HT/VHT due to WEP/TKIP use\n");
@@ -3516,7 +3564,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}
if (req->flags & ASSOC_REQ_DISABLE_HT) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
}
@@ -3524,7 +3572,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[req->bss->channel->band];
if (!sband->ht_cap.ht_supported ||
local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
if (!bss->wmm_used)
netdev_info(sdata->dev,
"disabling HT as WMM/QoS is not supported by the AP\n");
@@ -3569,7 +3617,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->ap_ht_param =
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
else
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
if (bss->wmm_used && bss->uapsd_supported &&
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
@@ -3660,40 +3708,44 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
bool tx = !req->local_state_change;
+ bool sent_frame = false;
mutex_lock(&ifmgd->mtx);
- if (ifmgd->auth_data) {
- ieee80211_destroy_auth_data(sdata, false);
- mutex_unlock(&ifmgd->mtx);
- return 0;
- }
-
sdata_info(sdata,
"deauthenticating from %pM by local choice (reason=%d)\n",
req->bssid, req->reason_code);
- if (ifmgd->associated &&
- ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
- ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
- req->reason_code, tx, frame_buf);
- } else {
+ if (ifmgd->auth_data) {
drv_mgd_prepare_tx(sdata->local, sdata);
ieee80211_send_deauth_disassoc(sdata, req->bssid,
IEEE80211_STYPE_DEAUTH,
req->reason_code, tx,
frame_buf);
+ ieee80211_destroy_auth_data(sdata, false);
+ mutex_unlock(&ifmgd->mtx);
+
+ sent_frame = tx;
+ goto out;
}
+ if (ifmgd->associated &&
+ ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+ req->reason_code, tx, frame_buf);
+ sent_frame = tx;
+ }
mutex_unlock(&ifmgd->mtx);
- __cfg80211_send_deauth(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
-
+ out:
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
+ if (sent_frame)
+ __cfg80211_send_deauth(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
+
return 0;
}
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index c349f3aaf59e..5abddfe3e101 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -204,9 +204,9 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
roc->frame = NULL;
}
} else {
- cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc,
- roc->chan, roc->chan_type,
- roc->req_duration, GFP_KERNEL);
+ cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie,
+ roc->chan, roc->req_duration,
+ GFP_KERNEL);
}
roc->notified = true;
@@ -283,8 +283,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
if (!duration)
duration = 10;
- ret = drv_remain_on_channel(local, roc->chan,
- roc->chan_type,
+ ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
duration);
roc->started = true;
@@ -320,8 +319,7 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
if (!roc->mgmt_tx_cookie)
cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
- (unsigned long)roc,
- roc->chan, roc->chan_type,
+ roc->cookie, roc->chan,
GFP_KERNEL);
list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
@@ -360,7 +358,6 @@ void ieee80211_sw_roc_work(struct work_struct *work)
ieee80211_recalc_idle(local);
local->tmp_channel = roc->chan;
- local->tmp_channel_type = roc->chan_type;
ieee80211_hw_config(local, 0);
/* tell userspace or send frame */
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 9f404ac901ab..79a48f37d409 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -33,6 +33,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
+ struct ieee80211_chanctx *ctx;
if (!local->open_count)
goto suspend;
@@ -135,14 +136,55 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_BEACON_ENABLED);
- /* the interface is leaving the channel and is removed */
- ieee80211_vif_release_channel(sdata);
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ rcu_access_pointer(sdata->u.ap.beacon))
+ drv_stop_ap(local, sdata);
+
+ if (local->use_chanctx) {
+ struct ieee80211_chanctx_conf *conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(
+ sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (conf) {
+ ctx = container_of(conf,
+ struct ieee80211_chanctx,
+ conf);
+ drv_unassign_vif_chanctx(local, sdata, ctx);
+ }
+
+ mutex_unlock(&local->chanctx_mtx);
+ }
drv_remove_interface(local, sdata);
}
sdata = rtnl_dereference(local->monitor_sdata);
- if (sdata)
+ if (sdata) {
+ if (local->use_chanctx) {
+ struct ieee80211_chanctx_conf *conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(
+ sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (conf) {
+ ctx = container_of(conf,
+ struct ieee80211_chanctx,
+ conf);
+ drv_unassign_vif_chanctx(local, sdata, ctx);
+ }
+
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
drv_remove_interface(local, sdata);
+ }
+
+ mutex_lock(&local->chanctx_mtx);
+ list_for_each_entry(ctx, &local->chanctx_list, list)
+ drv_remove_chanctx(local, ctx);
+ mutex_unlock(&local->chanctx_mtx);
/* stop hardware - this must stop RX */
if (local->open_count)
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 3313c117b322..dd88381c53b7 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -391,7 +391,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
return;
/* if HT BSS, and we handle a data frame, also try HT rates */
- if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT)
+ if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return;
fc = hdr->frame_control;
@@ -408,8 +408,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
alt_rate.flags |= IEEE80211_TX_RC_MCS;
- if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) ||
- (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS))
+ if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_40)
alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) {
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index ec198ef6aa8a..301386dabf88 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -65,7 +65,7 @@ static inline void rate_control_rate_init(struct sta_info *sta)
return;
}
- sband = local->hw.wiphy->bands[chanctx_conf->channel->band];
+ sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
rcu_read_unlock();
ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 8c1f1527d671..825f33cf7bbc 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -40,6 +40,8 @@
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
struct sk_buff *skb)
{
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN))
__pskb_trim(skb, skb->len - FCS_LEN);
@@ -51,20 +53,25 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
}
}
+ if (status->vendor_radiotap_len)
+ __pskb_pull(skb, status->vendor_radiotap_len);
+
return skb;
}
-static inline int should_drop_frame(struct sk_buff *skb,
- int present_fcs_len)
+static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (void *)(skb->data + status->vendor_radiotap_len);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC |
RX_FLAG_AMPDU_IS_ZEROLEN))
return 1;
- if (unlikely(skb->len < 16 + present_fcs_len))
+ if (unlikely(skb->len < 16 + present_fcs_len +
+ status->vendor_radiotap_len))
return 1;
if (ieee80211_is_ctl(hdr->frame_control) &&
!ieee80211_is_pspoll(hdr->frame_control) &&
@@ -74,32 +81,48 @@ static inline int should_drop_frame(struct sk_buff *skb,
}
static int
-ieee80211_rx_radiotap_len(struct ieee80211_local *local,
- struct ieee80211_rx_status *status)
+ieee80211_rx_radiotap_space(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status)
{
int len;
/* always present fields */
len = sizeof(struct ieee80211_radiotap_header) + 9;
- if (status->flag & RX_FLAG_MACTIME_MPDU)
+ /* allocate extra bitmap */
+ if (status->vendor_radiotap_len)
+ len += 4;
+
+ if (ieee80211_have_rx_timestamp(status)) {
+ len = ALIGN(len, 8);
len += 8;
+ }
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
len += 1;
- if (len & 1) /* padding for RX_FLAGS if necessary */
- len++;
+ /* padding for RX_FLAGS if necessary */
+ len = ALIGN(len, 2);
if (status->flag & RX_FLAG_HT) /* HT info */
len += 3;
if (status->flag & RX_FLAG_AMPDU_DETAILS) {
- /* padding */
- while (len & 3)
- len++;
+ len = ALIGN(len, 4);
len += 8;
}
+ if (status->vendor_radiotap_len) {
+ if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
+ status->vendor_radiotap_align = 1;
+ /* align standard part of vendor namespace */
+ len = ALIGN(len, 2);
+ /* allocate standard part of vendor namespace */
+ len += 6;
+ /* align vendor-defined part */
+ len = ALIGN(len, status->vendor_radiotap_align);
+ /* vendor-defined part is already in skb */
+ }
+
return len;
}
@@ -118,6 +141,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
u16 rx_flags = 0;
+ int mpdulen;
+
+ mpdulen = skb->len;
+ if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
+ mpdulen += FCS_LEN;
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
memset(rthdr, 0, rtap_len);
@@ -128,17 +156,30 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_ANTENNA) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
- rthdr->it_len = cpu_to_le16(rtap_len);
+ rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
- pos = (unsigned char *)(rthdr+1);
+ pos = (unsigned char *)(rthdr + 1);
+
+ if (status->vendor_radiotap_len) {
+ rthdr->it_present |=
+ cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
+ cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
+ put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
+ pos += 4;
+ }
/* the order of the following fields is important */
/* IEEE80211_RADIOTAP_TSFT */
- if (status->flag & RX_FLAG_MACTIME_MPDU) {
- put_unaligned_le64(status->mactime, pos);
- rthdr->it_present |=
- cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+ if (ieee80211_have_rx_timestamp(status)) {
+ /* padding */
+ while ((pos - (u8 *)rthdr) & 7)
+ *pos++ = 0;
+ put_unaligned_le64(
+ ieee80211_calculate_rx_timestamp(local, status,
+ mpdulen, 0),
+ pos);
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
pos += 8;
}
@@ -152,7 +193,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_RATE */
- if (!rate || status->flag & RX_FLAG_HT) {
+ if (!rate || status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) {
/*
* Without rate information don't add it. If we have,
* MCS information is a separate field in radiotap,
@@ -172,7 +213,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
if (status->band == IEEE80211_BAND_5GHZ)
put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
pos);
- else if (status->flag & RX_FLAG_HT)
+ else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
pos);
else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
@@ -205,7 +246,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_RX_FLAGS */
/* ensure 2 byte alignment for the 2 byte field as required */
if ((pos - (u8 *)rthdr) & 1)
- pos++;
+ *pos++ = 0;
if (status->flag & RX_FLAG_FAILED_PLCP_CRC)
rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP;
put_unaligned_le16(rx_flags, pos);
@@ -255,6 +296,21 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = 0;
*pos++ = 0;
}
+
+ if (status->vendor_radiotap_len) {
+ /* ensure 2 byte alignment for the vendor field as required */
+ if ((pos - (u8 *)rthdr) & 1)
+ *pos++ = 0;
+ *pos++ = status->vendor_radiotap_oui[0];
+ *pos++ = status->vendor_radiotap_oui[1];
+ *pos++ = status->vendor_radiotap_oui[2];
+ *pos++ = status->vendor_radiotap_subns;
+ put_unaligned_le16(status->vendor_radiotap_len, pos);
+ pos += 2;
+ /* align the actual payload as requested */
+ while ((pos - (u8 *)rthdr) & (status->vendor_radiotap_align - 1))
+ *pos++ = 0;
+ }
}
/*
@@ -283,13 +339,13 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
*/
/* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_len(local, status);
+ needed_headroom = ieee80211_rx_radiotap_space(local, status);
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
- /* make sure hdr->frame_control is on the linear part */
- if (!pskb_may_pull(origskb, 2)) {
+ /* ensure hdr->frame_control and vendor radiotap data are in skb head */
+ if (!pskb_may_pull(origskb, 2 + status->vendor_radiotap_len)) {
dev_kfree_skb(origskb);
return NULL;
}
@@ -374,7 +430,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return origskb;
}
-
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
@@ -403,10 +458,10 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
*
* We also use that counter for non-QoS STAs.
*/
- seqno_idx = NUM_RX_DATA_QUEUES;
+ seqno_idx = IEEE80211_NUM_TIDS;
security_idx = 0;
if (ieee80211_is_mgmt(hdr->frame_control))
- security_idx = NUM_RX_DATA_QUEUES;
+ security_idx = IEEE80211_NUM_TIDS;
tid = 0;
}
@@ -481,8 +536,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;
struct ieee80211_mmie *mmie;
- if (skb->len < 24 + sizeof(*mmie) ||
- !is_multicast_ether_addr(hdr->da))
+ if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
return -1;
if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr))
@@ -497,9 +551,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
return le16_to_cpu(mmie->key_id);
}
-
-static ieee80211_rx_result
-ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
+static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
char *dev_addr = rx->sdata->vif.addr;
@@ -507,7 +559,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) {
if (is_multicast_ether_addr(hdr->addr1)) {
if (ieee80211_has_tods(hdr->frame_control) ||
- !ieee80211_has_fromds(hdr->frame_control))
+ !ieee80211_has_fromds(hdr->frame_control))
return RX_DROP_MONITOR;
if (ether_addr_equal(hdr->addr3, dev_addr))
return RX_DROP_MONITOR;
@@ -539,7 +591,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
mgmt = (struct ieee80211_mgmt *)hdr;
category = mgmt->u.action.category;
if (category != WLAN_CATEGORY_MESH_ACTION &&
- category != WLAN_CATEGORY_SELF_PROTECTED)
+ category != WLAN_CATEGORY_SELF_PROTECTED)
return RX_DROP_MONITOR;
return RX_CONTINUE;
}
@@ -551,7 +603,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
return RX_DROP_MONITOR;
-
}
return RX_CONTINUE;
@@ -575,7 +626,6 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
return (sq1 - sq2) & SEQ_MASK;
}
-
static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx,
int index)
@@ -1291,17 +1341,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
/*
* Update last_rx only for IBSS packets which are for the current
- * BSSID to avoid keeping the current IBSS network alive in cases
- * where other STAs start using different BSSID.
+ * BSSID and for station already AUTHORIZED to avoid keeping the
+ * current IBSS network alive in cases where other STAs start
+ * using different BSSID. This will also give the station another
+ * chance to restart the authentication/authorization in case
+ * something went wrong the first time.
*/
if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) {
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
NL80211_IFTYPE_ADHOC);
- if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid)) {
+ if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) &&
+ test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
sta->last_rx = jiffies;
if (ieee80211_is_data(hdr->frame_control)) {
sta->last_rx_rate_idx = status->rate_idx;
sta->last_rx_rate_flag = status->flag;
+ sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
} else if (!is_multicast_ether_addr(hdr->addr1)) {
@@ -1313,6 +1368,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) {
sta->last_rx_rate_idx = status->rate_idx;
sta->last_rx_rate_flag = status->flag;
+ sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
@@ -1585,18 +1641,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
-static int
-ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
+static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
{
- if (unlikely(!rx->sta ||
- !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
+ if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
return -EACCES;
return 0;
}
-static int
-ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
+static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -1618,8 +1671,7 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
return 0;
}
-static int
-ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
+static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
@@ -2003,7 +2055,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
} else {
/* unable to resolve next hop */
mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3,
- 0, reason, fwd_hdr->addr2, sdata);
+ 0, reason, fwd_hdr->addr2, sdata);
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
kfree_skb(fwd_skb);
return RX_DROP_MONITOR;
@@ -2212,7 +2264,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
cfg80211_report_obss_beacon(rx->local->hw.wiphy,
rx->skb->data, rx->skb->len,
- status->freq, sig, GFP_ATOMIC);
+ status->freq, sig);
rx->flags |= IEEE80211_RX_BEACON_REPORTED;
}
@@ -2412,7 +2464,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (!ieee80211_vif_is_mesh(&sdata->vif))
break;
if (mesh_action_is_path_sel(mgmt) &&
- (!mesh_path_sel_is_hwmp(sdata)))
+ !mesh_path_sel_is_hwmp(sdata))
break;
goto queue;
}
@@ -2468,7 +2520,6 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
-
return RX_CONTINUE;
}
@@ -2598,7 +2649,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
goto out_free_skb;
/* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_len(local, status);
+ needed_headroom = ieee80211_rx_radiotap_space(local, status);
if (skb_headroom(skb) < needed_headroom &&
pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC))
@@ -2661,7 +2712,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
status = IEEE80211_SKB_RXCB((rx->skb));
sband = rx->local->hw.wiphy->bands[status->band];
- if (!(status->flag & RX_FLAG_HT))
+ if (!(status->flag & RX_FLAG_HT) &&
+ !(status->flag & RX_FLAG_VHT))
rate = &sband->bitrates[status->rate_idx];
ieee80211_rx_cooked_monitor(rx, rate);
@@ -2828,8 +2880,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!rx->sta) {
int rate_idx;
- if (status->flag & RX_FLAG_HT)
- rate_idx = 0; /* TODO: HT rates */
+ if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
+ rate_idx = 0; /* TODO: HT/VHT rates */
else
rate_idx = status->rate_idx;
ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2,
@@ -3105,6 +3157,13 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
status->rate_idx,
status->rate_idx))
goto drop;
+ } else if (status->flag & RX_FLAG_VHT) {
+ if (WARN_ONCE(status->rate_idx > 9 ||
+ !status->vht_nss ||
+ status->vht_nss > 8,
+ "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n",
+ status->rate_idx, status->vht_nss))
+ goto drop;
} else {
if (WARN_ON(status->rate_idx >= sband->n_bitrates))
goto drop;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 8e9bb168b73b..f3340279aba3 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -174,7 +174,6 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
u8 *elements;
struct ieee80211_channel *channel;
size_t baselen;
- int freq;
bool beacon;
struct ieee802_11_elems elems;
@@ -209,13 +208,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
- if (elems.ds_params && elems.ds_params_len == 1)
- freq = ieee80211_channel_to_frequency(elems.ds_params[0],
- rx_status->band);
- else
- freq = rx_status->freq;
-
- channel = ieee80211_get_channel(local->hw.wiphy, freq);
+ channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index e9d57689c05f..f3e502502fee 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -142,7 +142,7 @@ static void free_sta_work(struct work_struct *wk)
* drivers have to handle aggregation stop being requested, followed
* directly by station destruction.
*/
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
if (!tid_tx)
continue;
@@ -330,7 +330,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
return NULL;
}
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/*
* timer_to_tid must be initialized with identity mapping
* to enable session_timer's data differentiation. See
@@ -343,7 +343,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sta->tx_filtered[i]);
}
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
@@ -986,7 +986,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
clear_sta_flag(sta, WLAN_STA_SP);
- BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1);
+ BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
sta->driver_buffered_tids = 0;
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
@@ -1092,7 +1092,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
return;
}
- ieee80211_xmit(sdata, skb, chanctx_conf->channel->band);
+ ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band);
rcu_read_unlock();
}
@@ -1374,7 +1374,7 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
- if (WARN_ON(tid >= STA_TID_NUM))
+ if (WARN_ON(tid >= IEEE80211_NUM_TIDS))
return;
if (buffered)
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index c88f161f8118..6835cea4e402 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -80,7 +80,6 @@ enum ieee80211_sta_info_flags {
WLAN_STA_TOFFSET_KNOWN,
};
-#define STA_TID_NUM 16
#define ADDBA_RESP_INTERVAL HZ
#define HT_AGG_MAX_RETRIES 15
#define HT_AGG_BURST_RETRIES 3
@@ -197,15 +196,15 @@ struct tid_ampdu_rx {
struct sta_ampdu_mlme {
struct mutex mtx;
/* rx */
- struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM];
- unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)];
- unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)];
+ struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS];
+ unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
+ unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
/* tx */
struct work_struct work;
- struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM];
- struct tid_ampdu_tx *tid_start_tx[STA_TID_NUM];
- unsigned long last_addba_req_time[STA_TID_NUM];
- u8 addba_req_num[STA_TID_NUM];
+ struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
+ struct tid_ampdu_tx *tid_start_tx[IEEE80211_NUM_TIDS];
+ unsigned long last_addba_req_time[IEEE80211_NUM_TIDS];
+ u8 addba_req_num[IEEE80211_NUM_TIDS];
u8 dialog_token_allocator;
};
@@ -228,6 +227,7 @@ struct sta_ampdu_mlme {
* "the" transmit rate
* @last_rx_rate_idx: rx status rate index of the last data packet
* @last_rx_rate_flag: rx status flag of the last data packet
+ * @last_rx_rate_vht_nss: rx status nss of last data packet
* @lock: used for locking all fields that require locking, see comments
* in the header file.
* @drv_unblock_wk: used for driver PS unblocking
@@ -273,7 +273,7 @@ struct sta_ampdu_mlme {
* @t_offset: timing offset relative to this host
* @t_offset_setpoint: reference timing offset of this sta to be used when
* calculating clockdrift
- * @ch_type: peer's channel type
+ * @ch_width: peer's channel width
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver
@@ -330,7 +330,7 @@ struct sta_info {
int last_signal;
struct ewma avg_signal;
/* Plus 1 for non-QoS frames */
- __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1];
+ __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
/* Updated from TX status path only, no locking requirements */
unsigned long tx_filtered_count;
@@ -344,14 +344,15 @@ struct sta_info {
unsigned long tx_fragments;
struct ieee80211_tx_rate last_tx_rate;
int last_rx_rate_idx;
- int last_rx_rate_flag;
+ u32 last_rx_rate_flag;
+ u8 last_rx_rate_vht_nss;
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
/*
* Aggregation information, locked with lock.
*/
struct sta_ampdu_mlme ampdu_mlme;
- u8 timer_to_tid[STA_TID_NUM];
+ u8 timer_to_tid[IEEE80211_NUM_TIDS];
#ifdef CONFIG_MAC80211_MESH
/*
@@ -369,7 +370,7 @@ struct sta_info {
struct timer_list plink_timer;
s64 t_offset;
s64 t_offset_setpoint;
- enum nl80211_channel_type ch_type;
+ enum nl80211_chan_width ch_width;
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 248247877eb6..ab63237107c8 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -325,6 +325,75 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
}
+static void ieee80211_report_used_skb(struct ieee80211_local *local,
+ struct sk_buff *skb, bool dropped)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ bool acked = info->flags & IEEE80211_TX_STAT_ACK;
+
+ if (dropped)
+ acked = false;
+
+ if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
+ struct ieee80211_sub_if_data *sdata = NULL;
+ struct ieee80211_sub_if_data *iter_sdata;
+ u64 cookie = (unsigned long)skb;
+
+ rcu_read_lock();
+
+ if (skb->dev) {
+ list_for_each_entry_rcu(iter_sdata, &local->interfaces,
+ list) {
+ if (!iter_sdata->dev)
+ continue;
+
+ if (skb->dev == iter_sdata->dev) {
+ sdata = iter_sdata;
+ break;
+ }
+ }
+ } else {
+ sdata = rcu_dereference(local->p2p_sdata);
+ }
+
+ if (!sdata)
+ 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)) {
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ ack_skb = idr_find(&local->ack_status_frames,
+ info->ack_frame_id);
+ if (ack_skb)
+ idr_remove(&local->ack_status_frames,
+ info->ack_frame_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ if (ack_skb) {
+ if (!dropped) {
+ /* consumes ack_skb */
+ skb_complete_wifi_ack(ack_skb, acked);
+ } else {
+ dev_kfree_skb_any(ack_skb);
+ }
+ }
+ }
+}
+
/*
* Use a static threshold for now, best value to be determined
* by testing ...
@@ -516,62 +585,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
msecs_to_jiffies(10));
}
- if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
- u64 cookie = (unsigned long)skb;
- bool found = false;
-
- acked = info->flags & IEEE80211_TX_STAT_ACK;
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- if (!sdata->dev)
- continue;
-
- if (skb->dev != sdata->dev)
- continue;
-
- 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)) {
- struct sk_buff *ack_skb;
- unsigned long flags;
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- ack_skb = idr_find(&local->ack_status_frames,
- info->ack_frame_id);
- if (ack_skb)
- idr_remove(&local->ack_status_frames,
- info->ack_frame_id);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- /* consumes ack_skb */
- if (ack_skb)
- skb_complete_wifi_ack(ack_skb,
- info->flags & IEEE80211_TX_STAT_ACK);
- }
+ ieee80211_report_used_skb(local, skb, false);
/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);
@@ -647,25 +661,8 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);
void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- if (unlikely(info->ack_frame_id)) {
- struct sk_buff *ack_skb;
- unsigned long flags;
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- ack_skb = idr_find(&local->ack_status_frames,
- info->ack_frame_id);
- if (ack_skb)
- idr_remove(&local->ack_status_frames,
- info->ack_frame_id);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- /* consumes ack_skb */
- if (ack_skb)
- dev_kfree_skb_any(ack_skb);
- }
+ ieee80211_report_used_skb(local, skb, true);
dev_kfree_skb_any(skb);
}
EXPORT_SYMBOL(ieee80211_free_txskb);
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 0638541b625f..a8270b441a6f 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -28,16 +28,21 @@
#define VIF_PR_FMT " vif:%s(%d%s)"
#define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : ""
-#define CHANCTX_ENTRY __field(int, freq) \
- __field(int, chantype) \
+#define CHANCTX_ENTRY __field(u32, control_freq) \
+ __field(u32, chan_width) \
+ __field(u32, center_freq1) \
+ __field(u32, center_freq2) \
__field(u8, rx_chains_static) \
__field(u8, rx_chains_dynamic)
-#define CHANCTX_ASSIGN __entry->freq = ctx->conf.channel->center_freq; \
- __entry->chantype = ctx->conf.channel_type; \
+#define CHANCTX_ASSIGN __entry->control_freq = ctx->conf.def.chan->center_freq;\
+ __entry->chan_width = ctx->conf.def.width; \
+ __entry->center_freq1 = ctx->conf.def.center_freq1; \
+ __entry->center_freq2 = ctx->conf.def.center_freq2; \
__entry->rx_chains_static = ctx->conf.rx_chains_static; \
__entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic
-#define CHANCTX_PR_FMT " freq:%d MHz chantype:%d chains:%d/%d"
-#define CHANCTX_PR_ARG __entry->freq, __entry->chantype, \
+#define CHANCTX_PR_FMT " control:%d MHz width:%d center: %d/%d MHz chains:%d/%d"
+#define CHANCTX_PR_ARG __entry->control_freq, __entry->chan_width, \
+ __entry->center_freq1, __entry->center_freq2, \
__entry->rx_chains_static, __entry->rx_chains_dynamic
@@ -334,7 +339,8 @@ TRACE_EVENT(drv_bss_info_changed,
__field(u16, ht_operation_mode)
__field(s32, cqm_rssi_thold);
__field(s32, cqm_rssi_hyst);
- __field(u32, channel_type);
+ __field(u32, channel_width);
+ __field(u32, channel_cfreq1);
__dynamic_array(u32, arp_addr_list, info->arp_addr_cnt);
__field(bool, arp_filter_enabled);
__field(bool, qos);
@@ -342,6 +348,9 @@ TRACE_EVENT(drv_bss_info_changed,
__field(bool, ps);
__dynamic_array(u8, ssid, info->ssid_len);
__field(bool, hidden_ssid);
+ __field(int, txpower)
+ __field(u8, p2p_ctwindow)
+ __field(bool, p2p_oppps)
),
TP_fast_assign(
@@ -367,7 +376,8 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->ht_operation_mode = info->ht_operation_mode;
__entry->cqm_rssi_thold = info->cqm_rssi_thold;
__entry->cqm_rssi_hyst = info->cqm_rssi_hyst;
- __entry->channel_type = info->channel_type;
+ __entry->channel_width = info->chandef.width;
+ __entry->channel_cfreq1 = info->chandef.center_freq1;
memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list,
sizeof(u32) * info->arp_addr_cnt);
__entry->arp_filter_enabled = info->arp_filter_enabled;
@@ -376,6 +386,9 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->ps = info->ps;
memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
__entry->hidden_ssid = info->hidden_ssid;
+ __entry->txpower = info->txpower;
+ __entry->p2p_ctwindow = info->p2p_ctwindow;
+ __entry->p2p_oppps = info->p2p_oppps;
),
TP_printk(
@@ -1013,28 +1026,31 @@ TRACE_EVENT(drv_get_antenna,
);
TRACE_EVENT(drv_remain_on_channel,
- TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel *chan,
- enum nl80211_channel_type chantype, unsigned int duration),
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *chan,
+ unsigned int duration),
- TP_ARGS(local, chan, chantype, duration),
+ TP_ARGS(local, sdata, chan, duration),
TP_STRUCT__entry(
LOCAL_ENTRY
+ VIF_ENTRY
__field(int, center_freq)
- __field(int, channel_type)
__field(unsigned int, duration)
),
TP_fast_assign(
LOCAL_ASSIGN;
+ VIF_ASSIGN;
__entry->center_freq = chan->center_freq;
- __entry->channel_type = chantype;
__entry->duration = duration;
),
TP_printk(
- LOCAL_PR_FMT " freq:%dMHz duration:%dms",
- LOCAL_PR_ARG, __entry->center_freq, __entry->duration
+ LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms",
+ LOCAL_PR_ARG, VIF_PR_ARG,
+ __entry->center_freq, __entry->duration
)
);
@@ -1043,34 +1059,6 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel,
TP_ARGS(local)
);
-TRACE_EVENT(drv_offchannel_tx,
- TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
- unsigned int wait),
-
- TP_ARGS(local, skb, chan, channel_type, wait),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- __field(int, center_freq)
- __field(int, channel_type)
- __field(unsigned int, wait)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- __entry->center_freq = chan->center_freq;
- __entry->channel_type = channel_type;
- __entry->wait = wait;
- ),
-
- TP_printk(
- LOCAL_PR_FMT " freq:%dMHz, wait:%dms",
- LOCAL_PR_ARG, __entry->center_freq, __entry->wait
- )
-);
-
TRACE_EVENT(drv_set_ringparam,
TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx),
@@ -1396,6 +1384,48 @@ DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx,
TP_ARGS(local, sdata, ctx)
);
+TRACE_EVENT(drv_start_ap,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *info),
+
+ TP_ARGS(local, sdata, info),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(u8, dtimper)
+ __field(u16, bcnint)
+ __dynamic_array(u8, ssid, info->ssid_len);
+ __field(bool, hidden_ssid);
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ __entry->dtimper = info->dtim_period;
+ __entry->bcnint = info->beacon_int;
+ memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
+ __entry->hidden_ssid = info->hidden_ssid;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG
+ )
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
+
+DEFINE_EVENT(local_only_evt, drv_restart_complete,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
/*
* Tracing for API calls that drivers call.
*/
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 6f1981816dc5..d287a4f2c01b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1676,7 +1676,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
if (!chanctx_conf)
goto fail_rcu;
- chan = chanctx_conf->channel;
+ chan = chanctx_conf->def.chan;
/*
* Frame injection is not allowed if beaconing is not allowed
@@ -1779,7 +1779,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
if (!chanctx_conf)
goto fail_rcu;
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
if (sta)
break;
/* fall through */
@@ -1794,7 +1794,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf)
goto fail_rcu;
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_WDS:
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
@@ -1871,7 +1871,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf)
goto fail_rcu;
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
break;
#endif
case NL80211_IFTYPE_STATION:
@@ -1930,7 +1930,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf)
goto fail_rcu;
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
@@ -1941,7 +1941,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf)
goto fail_rcu;
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
break;
default:
goto fail_rcu;
@@ -2089,6 +2089,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
head_need = max_t(int, 0, head_need);
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb);
+ skb = NULL;
goto fail_rcu;
}
}
@@ -2193,7 +2194,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
return true;
}
result = ieee80211_tx(sdata, skb, true,
- chanctx_conf->channel->band);
+ chanctx_conf->def.chan->band);
} else {
struct sk_buff_head skbs;
@@ -2457,7 +2458,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*pos++ = WLAN_EID_SSID;
*pos++ = 0x0;
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
mesh_add_ds_params_ie(skb, sdata) ||
@@ -2476,7 +2477,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
goto out;
}
- band = chanctx_conf->channel->band;
+ band = chanctx_conf->def.chan->band;
info = IEEE80211_SKB_CB(skb);
@@ -2756,7 +2757,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
info = IEEE80211_SKB_CB(skb);
tx.flags |= IEEE80211_TX_PS_BUFFERED;
- info->band = chanctx_conf->channel->band;
+ info->band = chanctx_conf->def.chan->band;
if (invoke_tx_handlers(&tx))
skb = NULL;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index acbb8c9bae2a..08132ff98155 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw)
EXPORT_SYMBOL(ieee80211_wake_queues);
void ieee80211_iterate_active_interfaces(
- struct ieee80211_hw *hw,
+ struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
@@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces(
default:
break;
}
+ if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
+ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ continue;
if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr,
&sdata->vif);
@@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces(
sdata = rcu_dereference_protected(local->monitor_sdata,
lockdep_is_held(&local->iflist_mtx));
- if (sdata)
+ if (sdata &&
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
mutex_unlock(&local->iflist_mtx);
@@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces(
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
void ieee80211_iterate_active_interfaces_atomic(
- struct ieee80211_hw *hw,
+ struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
@@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic(
default:
break;
}
+ if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
+ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ continue;
if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr,
&sdata->vif);
}
sdata = rcu_dereference(local->monitor_sdata);
- if (sdata)
+ if (sdata &&
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
rcu_read_unlock();
@@ -888,7 +898,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
use_11b = (chanctx_conf &&
- chanctx_conf->channel->band == IEEE80211_BAND_2GHZ) &&
+ chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) &&
!(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
rcu_read_unlock();
@@ -981,7 +991,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf &&
- chanctx_conf->channel->band == IEEE80211_BAND_2GHZ &&
+ chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
@@ -1407,10 +1417,44 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
/* add channel contexts */
- mutex_lock(&local->chanctx_mtx);
- list_for_each_entry(ctx, &local->chanctx_list, list)
- WARN_ON(drv_add_chanctx(local, ctx));
- mutex_unlock(&local->chanctx_mtx);
+ if (local->use_chanctx) {
+ mutex_lock(&local->chanctx_mtx);
+ list_for_each_entry(ctx, &local->chanctx_list, list)
+ WARN_ON(drv_add_chanctx(local, ctx));
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ struct ieee80211_chanctx_conf *ctx_conf;
+
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+
+ mutex_lock(&local->chanctx_mtx);
+ ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (ctx_conf) {
+ ctx = container_of(ctx_conf, struct ieee80211_chanctx,
+ conf);
+ drv_assign_vif_chanctx(local, sdata, ctx);
+ }
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
+ sdata = rtnl_dereference(local->monitor_sdata);
+ if (sdata && local->use_chanctx && ieee80211_sdata_running(sdata)) {
+ struct ieee80211_chanctx_conf *ctx_conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (ctx_conf) {
+ ctx = container_of(ctx_conf, struct ieee80211_chanctx,
+ conf);
+ drv_assign_vif_chanctx(local, sdata, ctx);
+ }
+ mutex_unlock(&local->chanctx_mtx);
+ }
/* add STAs back */
mutex_lock(&local->sta_mtx);
@@ -1452,22 +1496,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* Finally also reconfigure all the BSS information */
list_for_each_entry(sdata, &local->interfaces, list) {
- struct ieee80211_chanctx_conf *ctx_conf;
u32 changed;
if (!ieee80211_sdata_running(sdata))
continue;
- mutex_lock(&local->chanctx_mtx);
- ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
- lockdep_is_held(&local->chanctx_mtx));
- if (ctx_conf) {
- ctx = container_of(ctx_conf, struct ieee80211_chanctx,
- conf);
- drv_assign_vif_chanctx(local, sdata, ctx);
- }
- mutex_unlock(&local->chanctx_mtx);
-
/* common change flags for all interface types */
changed = BSS_CHANGED_ERP_CTS_PROT |
BSS_CHANGED_ERP_PREAMBLE |
@@ -1478,7 +1511,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
BSS_CHANGED_BSSID |
BSS_CHANGED_CQM |
BSS_CHANGED_QOS |
- BSS_CHANGED_IDLE;
+ BSS_CHANGED_IDLE |
+ BSS_CHANGED_TXPOWER;
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
@@ -1495,9 +1529,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
case NL80211_IFTYPE_AP:
changed |= BSS_CHANGED_SSID;
- if (sdata->vif.type == NL80211_IFTYPE_AP)
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
changed |= BSS_CHANGED_AP_PROBE_RESP;
+ if (rcu_access_pointer(sdata->u.ap.beacon))
+ drv_start_ap(local, sdata);
+ }
+
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
changed |= BSS_CHANGED_BEACON |
@@ -1596,8 +1634,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
* If this is for hw restart things are still running.
* We may want to change that later, however.
*/
- if (!local->suspended)
+ if (!local->suspended) {
+ drv_restart_complete(local);
return 0;
+ }
#ifdef CONFIG_PM
/* first set suspended false, then resuming */
@@ -1833,8 +1873,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
}
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
u16 prot_mode)
{
struct ieee80211_ht_operation *ht_oper;
@@ -1842,23 +1881,25 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
*pos++ = WLAN_EID_HT_OPERATION;
*pos++ = sizeof(struct ieee80211_ht_operation);
ht_oper = (struct ieee80211_ht_operation *)pos;
- ht_oper->primary_chan =
- ieee80211_frequency_to_channel(channel->center_freq);
- switch (channel_type) {
- case NL80211_CHAN_HT40MINUS:
- ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- break;
- case NL80211_CHAN_HT40PLUS:
- ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ ht_oper->primary_chan = ieee80211_frequency_to_channel(
+ chandef->chan->center_freq);
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 > chandef->chan->center_freq)
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ else
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
break;
- case NL80211_CHAN_HT20:
default:
ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
break;
}
if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
- channel_type != NL80211_CHAN_NO_HT &&
- channel_type != NL80211_CHAN_HT20)
+ chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
+ chandef->width != NL80211_CHAN_WIDTH_20)
ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
ht_oper->operation_mode = cpu_to_le16(prot_mode);
@@ -1872,13 +1913,17 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
return pos + sizeof(struct ieee80211_ht_operation);
}
-enum nl80211_channel_type
-ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper)
+void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
+ struct ieee80211_ht_operation *ht_oper,
+ struct cfg80211_chan_def *chandef)
{
enum nl80211_channel_type channel_type;
- if (!ht_oper)
- return NL80211_CHAN_NO_HT;
+ if (!ht_oper) {
+ cfg80211_chandef_create(chandef, control_chan,
+ NL80211_CHAN_NO_HT);
+ return;
+ }
switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
@@ -1894,7 +1939,7 @@ ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper)
channel_type = NL80211_CHAN_NO_HT;
}
- return channel_type;
+ cfg80211_chandef_create(chandef, control_chan, channel_type);
}
int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
@@ -1992,3 +2037,68 @@ u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
return 2;
return 1;
}
+
+/**
+ * ieee80211_calculate_rx_timestamp - calculate timestamp in frame
+ * @local: mac80211 hw info struct
+ * @status: RX status
+ * @mpdu_len: total MPDU length (including FCS)
+ * @mpdu_offset: offset into MPDU to calculate timestamp at
+ *
+ * This function calculates the RX timestamp at the given MPDU offset, taking
+ * into account what the RX timestamp was. An offset of 0 will just normalize
+ * the timestamp to TSF at beginning of MPDU reception.
+ */
+u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status,
+ unsigned int mpdu_len,
+ unsigned int mpdu_offset)
+{
+ u64 ts = status->mactime;
+ struct rate_info ri;
+ u16 rate;
+
+ if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
+ return 0;
+
+ memset(&ri, 0, sizeof(ri));
+
+ /* Fill cfg80211 rate info */
+ if (status->flag & RX_FLAG_HT) {
+ ri.mcs = status->rate_idx;
+ ri.flags |= RATE_INFO_FLAGS_MCS;
+ if (status->flag & RX_FLAG_40MHZ)
+ ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_SHORT_GI)
+ ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ } else if (status->flag & RX_FLAG_VHT) {
+ ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
+ ri.mcs = status->rate_idx;
+ ri.nss = status->vht_nss;
+ if (status->flag & RX_FLAG_40MHZ)
+ ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_80MHZ)
+ ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_80P80MHZ)
+ ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_160MHZ)
+ ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_SHORT_GI)
+ ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+ sband = local->hw.wiphy->bands[status->band];
+ ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+ }
+
+ rate = cfg80211_calculate_bitrate(&ri);
+
+ /* rewind from end of MPDU */
+ if (status->flag & RX_FLAG_MACTIME_END)
+ ts -= mpdu_len * 8 * 10 / rate;
+
+ ts += mpdu_offset * 8 * 10 / rate;
+
+ return ts;
+}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index cea06e9f26f4..906f00cd6d2f 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -160,31 +160,37 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
return ieee80211_downgrade_queue(sdata, skb);
}
+/**
+ * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
+ *
+ * @sdata: local subif
+ * @skb: packet to be updated
+ */
void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ u8 *p;
+ u8 ack_policy, tid;
- /* Fill in the QoS header if there is one. */
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- u8 *p = ieee80211_get_qos_ctl(hdr);
- u8 ack_policy, tid;
-
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ return;
- /* preserve EOSP bit */
- ack_policy = *p & IEEE80211_QOS_CTL_EOSP;
+ p = ieee80211_get_qos_ctl(hdr);
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
- if (is_multicast_ether_addr(hdr->addr1) ||
- sdata->noack_map & BIT(tid)) {
- ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
- info->flags |= IEEE80211_TX_CTL_NO_ACK;
- }
+ /* preserve EOSP bit */
+ ack_policy = *p & IEEE80211_QOS_CTL_EOSP;
- /* qos header is 2 bytes */
- *p++ = ack_policy | tid;
- *p = ieee80211_vif_is_mesh(&sdata->vif) ?
- (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
+ if (is_multicast_ether_addr(hdr->addr1) ||
+ sdata->noack_map & BIT(tid)) {
+ ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
+ info->flags |= IEEE80211_TX_CTL_NO_ACK;
}
+
+ /* qos header is 2 bytes */
+ *p++ = ack_policy | tid;
+ *p = ieee80211_vif_is_mesh(&sdata->vif) ?
+ (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
}
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 07659cfd6d7b..7d99410e6c1a 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -344,7 +344,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
return -EADDRINUSE;
if (pipe != NFC_HCI_INVALID_PIPE)
- goto pipe_is_open;
+ goto open_pipe;
switch (dest_gate) {
case NFC_HCI_LINK_MGMT_GATE:
@@ -361,6 +361,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
break;
}
+open_pipe:
r = nfc_hci_open_pipe(hdev, pipe);
if (r < 0) {
if (pipe_created)
@@ -371,7 +372,6 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
return r;
}
-pipe_is_open:
hdev->gate2pipe[dest_gate] = pipe;
return 0;
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index bc571b0efb92..7bea574d5934 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -33,17 +33,20 @@
/* Largest headroom needed for outgoing HCI commands */
#define HCI_CMDS_HEADROOM 1
-static int nfc_hci_result_to_errno(u8 result)
+int nfc_hci_result_to_errno(u8 result)
{
switch (result) {
case NFC_HCI_ANY_OK:
return 0;
+ case NFC_HCI_ANY_E_REG_PAR_UNKNOWN:
+ return -EOPNOTSUPP;
case NFC_HCI_ANY_E_TIMEOUT:
return -ETIME;
default:
return -1;
}
}
+EXPORT_SYMBOL(nfc_hci_result_to_errno);
static void nfc_hci_msg_tx_work(struct work_struct *work)
{
@@ -167,7 +170,7 @@ void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
kfree_skb(skb);
}
-static u32 nfc_hci_sak_to_protocol(u8 sak)
+u32 nfc_hci_sak_to_protocol(u8 sak)
{
switch (NFC_HCI_TYPE_A_SEL_PROT(sak)) {
case NFC_HCI_TYPE_A_SEL_PROT_MIFARE:
@@ -182,6 +185,7 @@ static u32 nfc_hci_sak_to_protocol(u8 sak)
return 0xffffffff;
}
}
+EXPORT_SYMBOL(nfc_hci_sak_to_protocol);
int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
{
@@ -284,6 +288,12 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
struct sk_buff *skb)
{
int r = 0;
+ u8 gate = nfc_hci_pipe2gate(hdev, pipe);
+
+ if (gate == 0xff) {
+ pr_err("Discarded event %x to unopened pipe %x\n", event, pipe);
+ goto exit;
+ }
switch (event) {
case NFC_HCI_EVT_TARGET_DISCOVERED:
@@ -307,14 +317,11 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
goto exit;
}
- r = nfc_hci_target_discovered(hdev,
- nfc_hci_pipe2gate(hdev, pipe));
+ r = nfc_hci_target_discovered(hdev, gate);
break;
default:
if (hdev->ops->event_received) {
- hdev->ops->event_received(hdev,
- nfc_hci_pipe2gate(hdev, pipe),
- event, skb);
+ hdev->ops->event_received(hdev, gate, event, skb);
return;
}
@@ -419,6 +426,10 @@ static int hci_dev_version(struct nfc_hci_dev *hdev)
r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
NFC_HCI_ID_MGMT_VERSION_SW, &skb);
+ if (r == -EOPNOTSUPP) {
+ pr_info("Software/Hardware info not available\n");
+ return 0;
+ }
if (r < 0)
return r;
diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c
index ed2d17312d61..df24be48d4da 100644
--- a/net/nfc/llcp/commands.c
+++ b/net/nfc/llcp/commands.c
@@ -528,6 +528,23 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
if (local == NULL)
return -ENODEV;
+ /* Remote is ready but has not acknowledged our frames */
+ if((sock->remote_ready &&
+ skb_queue_len(&sock->tx_pending_queue) >= sock->rw &&
+ skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) {
+ pr_err("Pending queue is full %d frames\n",
+ skb_queue_len(&sock->tx_pending_queue));
+ return -ENOBUFS;
+ }
+
+ /* Remote is not ready and we've been queueing enough frames */
+ if ((!sock->remote_ready &&
+ skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) {
+ pr_err("Tx queue is full %d frames\n",
+ skb_queue_len(&sock->tx_queue));
+ return -ENOBUFS;
+ }
+
msg_data = kzalloc(len, GFP_KERNEL);
if (msg_data == NULL)
return -ENOMEM;
@@ -579,7 +596,7 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
struct sk_buff *pdu;
struct nfc_llcp_local *local;
size_t frag_len = 0, remaining_len;
- u8 *msg_ptr;
+ u8 *msg_ptr, *msg_data;
int err;
pr_debug("Send UI frame len %zd\n", len);
@@ -588,8 +605,17 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
if (local == NULL)
return -ENODEV;
+ msg_data = kzalloc(len, GFP_KERNEL);
+ if (msg_data == NULL)
+ return -ENOMEM;
+
+ if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) {
+ kfree(msg_data);
+ return -EFAULT;
+ }
+
remaining_len = len;
- msg_ptr = (u8 *) msg->msg_iov;
+ msg_ptr = msg_data;
while (remaining_len > 0) {
@@ -616,6 +642,8 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
msg_ptr += frag_len;
}
+ kfree(msg_data);
+
return len;
}
diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c
index 2e9ddf34c099..2df87056c6df 100644
--- a/net/nfc/llcp/llcp.c
+++ b/net/nfc/llcp/llcp.c
@@ -656,6 +656,8 @@ static void nfc_llcp_tx_work(struct work_struct *work)
if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
nfc_llcp_send_symm(local->dev);
} else {
+ struct sk_buff *copy_skb = NULL;
+ u8 ptype = nfc_llcp_ptype(skb);
int ret;
pr_debug("Sending pending skb\n");
@@ -663,22 +665,29 @@ static void nfc_llcp_tx_work(struct work_struct *work)
DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb->len, true);
+ if (ptype == LLCP_PDU_I)
+ copy_skb = skb_copy(skb, GFP_ATOMIC);
+
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);
- if (!ret && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
- skb = skb_get(skb);
- skb_queue_tail(&llcp_sock->tx_pending_queue,
- skb);
+ if (ret) {
+ kfree_skb(copy_skb);
+ goto out;
}
+
+ if (ptype == LLCP_PDU_I && copy_skb)
+ skb_queue_tail(&llcp_sock->tx_pending_queue,
+ copy_skb);
}
} else {
nfc_llcp_send_symm(local->dev);
}
+out:
mod_timer(&local->link_timer,
jiffies + msecs_to_jiffies(2 * local->remote_lto));
}
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index fe4adb12b3ef..16d08b399210 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -140,14 +140,13 @@ config CFG80211_WEXT
extensions with cfg80211-based drivers.
config LIB80211
- tristate "Common routines for IEEE802.11 drivers"
+ tristate
default n
help
This options enables a library of common routines used
by IEEE802.11 wireless LAN drivers.
- Drivers should select this themselves if needed. Say Y if
- you want this built into your kernel.
+ Drivers should select this themselves if needed.
config LIB80211_CRYPT_WEP
tristate
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index e143505f05bc..324e8d851dc4 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -28,6 +28,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
if (!err) {
wdev->beacon_interval = 0;
wdev->channel = NULL;
+ wdev->ssid_len = 0;
}
return err;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 48febd2160ba..bf2dfd54ff3b 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -11,96 +11,264 @@
#include "core.h"
#include "rdev-ops.h"
-struct ieee80211_channel *
-rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
- int freq, enum nl80211_channel_type channel_type)
+void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type chan_type)
{
- struct ieee80211_channel *chan;
- struct ieee80211_sta_ht_cap *ht_cap;
+ if (WARN_ON(!chan))
+ return;
- chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ chandef->chan = chan;
+ chandef->center_freq2 = 0;
- /* Primary channel not allowed */
- if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
- return NULL;
+ switch (chan_type) {
+ case NL80211_CHAN_NO_HT:
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT20:
+ chandef->width = NL80211_CHAN_WIDTH_20;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq + 10;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq - 10;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+EXPORT_SYMBOL(cfg80211_chandef_create);
- if (channel_type == NL80211_CHAN_HT40MINUS &&
- chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
- return NULL;
- else if (channel_type == NL80211_CHAN_HT40PLUS &&
- chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
- return NULL;
+bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
+{
+ u32 control_freq;
- ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
+ if (!chandef->chan)
+ return false;
- if (channel_type != NL80211_CHAN_NO_HT) {
- if (!ht_cap->ht_supported)
- return NULL;
+ control_freq = chandef->chan->center_freq;
- if (channel_type != NL80211_CHAN_HT20 &&
- (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
- ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
- return NULL;
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ if (chandef->center_freq1 != control_freq)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ if (chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30)
+ return false;
+ if (!chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ if (chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ if (chandef->center_freq1 != control_freq + 70 &&
+ chandef->center_freq1 != control_freq + 50 &&
+ chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30 &&
+ chandef->center_freq1 != control_freq - 50 &&
+ chandef->center_freq1 != control_freq - 70)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ default:
+ return false;
}
- return chan;
+ return true;
}
-bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
+ int *pri40, int *pri80)
{
- struct ieee80211_channel *sec_chan;
- int diff;
-
- trace_cfg80211_can_beacon_sec_chan(wiphy, chan, channel_type);
+ int tmp;
- switch (channel_type) {
- case NL80211_CHAN_HT40PLUS:
- diff = 20;
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_40:
+ *pri40 = c->center_freq1;
+ *pri80 = 0;
break;
- case NL80211_CHAN_HT40MINUS:
- diff = -20;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ *pri80 = c->center_freq1;
+ /* n_P20 */
+ tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ *pri40 = c->center_freq1 - 20 + 40 * tmp;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ /* n_P20 */
+ tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ *pri40 = c->center_freq1 - 60 + 40 * tmp;
+ /* n_P80 */
+ tmp /= 2;
+ *pri80 = c->center_freq1 - 40 + 80 * tmp;
break;
default:
- trace_cfg80211_return_bool(true);
- return true;
+ WARN_ON_ONCE(1);
}
+}
+
+const struct cfg80211_chan_def *
+cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
+ const struct cfg80211_chan_def *c2)
+{
+ u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
- sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff);
- if (!sec_chan) {
+ /* If they are identical, return */
+ if (cfg80211_chandef_identical(c1, c2))
+ return c1;
+
+ /* otherwise, must have same control channel */
+ if (c1->chan != c2->chan)
+ return NULL;
+
+ /*
+ * If they have the same width, but aren't identical,
+ * then they can't be compatible.
+ */
+ if (c1->width == c2->width)
+ return NULL;
+
+ if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ c1->width == NL80211_CHAN_WIDTH_20)
+ return c2;
+
+ if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ c2->width == NL80211_CHAN_WIDTH_20)
+ return c1;
+
+ chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
+ chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
+
+ if (c1_pri40 != c2_pri40)
+ return NULL;
+
+ WARN_ON(!c1_pri80 && !c2_pri80);
+ if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
+ return NULL;
+
+ if (c1->width > c2->width)
+ return c1;
+ return c2;
+}
+EXPORT_SYMBOL(cfg80211_chandef_compatible);
+
+bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags)
+{
+ struct ieee80211_channel *c;
+ u32 freq;
+
+ for (freq = center_freq - bandwidth/2 + 10;
+ freq <= center_freq + bandwidth/2 - 10;
+ freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || c->flags & prohibited_flags)
+ return false;
+ }
+
+ return true;
+}
+
+static bool cfg80211_check_beacon_chans(struct wiphy *wiphy,
+ u32 center_freq, u32 bw)
+{
+ return cfg80211_secondary_chans_ok(wiphy, center_freq, bw,
+ IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_PASSIVE_SCAN |
+ IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_RADAR);
+}
+
+bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ u32 width;
+ bool res;
+
+ trace_cfg80211_reg_can_beacon(wiphy, chandef);
+
+ if (WARN_ON(!cfg80211_chan_def_valid(chandef))) {
trace_cfg80211_return_bool(false);
return false;
}
- /* we'll need a DFS capability later */
- if (sec_chan->flags & (IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_PASSIVE_SCAN |
- IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_RADAR)) {
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ width = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ width = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ width = 160;
+ break;
+ default:
+ WARN_ON_ONCE(1);
trace_cfg80211_return_bool(false);
return false;
}
- trace_cfg80211_return_bool(true);
- return true;
+
+ res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width);
+
+ if (res && chandef->center_freq2)
+ res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2,
+ width);
+
+ trace_cfg80211_return_bool(res);
+ return res;
}
-EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
+EXPORT_SYMBOL(cfg80211_reg_can_beacon);
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
- int freq, enum nl80211_channel_type chantype)
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_channel *chan;
-
if (!rdev->ops->set_monitor_channel)
return -EOPNOTSUPP;
if (!cfg80211_has_monitors_only(rdev))
return -EBUSY;
- chan = rdev_freq_to_chan(rdev, freq, chantype);
- if (!chan)
- return -EINVAL;
-
- return rdev_set_monitor_channel(rdev, chan, chantype);
+ return rdev_set_monitor_channel(rdev, chandef);
}
void
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 26711f46a3be..14d990400354 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -326,6 +326,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
mutex_init(&rdev->devlist_mtx);
mutex_init(&rdev->sched_scan_mtx);
INIT_LIST_HEAD(&rdev->wdev_list);
+ INIT_LIST_HEAD(&rdev->beacon_registrations);
+ spin_lock_init(&rdev->beacon_registrations_lock);
spin_lock_init(&rdev->bss_lock);
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
@@ -698,10 +700,15 @@ EXPORT_SYMBOL(wiphy_unregister);
void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
{
struct cfg80211_internal_bss *scan, *tmp;
+ struct cfg80211_beacon_registration *reg, *treg;
rfkill_destroy(rdev->rfkill);
mutex_destroy(&rdev->mtx);
mutex_destroy(&rdev->devlist_mtx);
mutex_destroy(&rdev->sched_scan_mtx);
+ list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
+ list_del(&reg->list);
+ kfree(reg);
+ }
list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
cfg80211_put_bss(&scan->pub);
kfree(rdev);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b8eb743fe7da..a0c8decf6a47 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -55,7 +55,8 @@ struct cfg80211_registered_device {
int opencount; /* also protected by devlist_mtx */
wait_queue_head_t dev_wait;
- u32 ap_beacons_nlportid;
+ struct list_head beacon_registrations;
+ spinlock_t beacon_registrations_lock;
/* protected by RTNL only */
int num_running_ifaces;
@@ -260,6 +261,10 @@ enum cfg80211_chan_mode {
CHAN_MODE_EXCLUSIVE,
};
+struct cfg80211_beacon_registration {
+ struct list_head list;
+ u32 nlportid;
+};
/* free object */
extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
@@ -304,9 +309,9 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
const struct mesh_config *conf);
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev);
-int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, int freq,
- enum nl80211_channel_type channel_type);
+int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef);
/* AP */
int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
@@ -373,10 +378,8 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck,
- bool dont_wait_for_ack, u64 *cookie);
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie);
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask);
@@ -467,11 +470,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
struct ieee80211_channel **chan,
enum cfg80211_chan_mode *chanmode);
-struct ieee80211_channel *
-rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
- int freq, enum nl80211_channel_type channel_type);
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
- int freq, enum nl80211_channel_type chantype);
+ struct cfg80211_chan_def *chandef);
int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
const u8 *rates, unsigned int n_rates,
@@ -483,6 +483,12 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype, int num);
+bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef);
+
+bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags);
+
#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 27941d5db72b..9b9551e4a6f9 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -100,9 +100,9 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
* 11a for maximum compatibility.
*/
struct ieee80211_supported_band *sband =
- rdev->wiphy.bands[params->channel->band];
+ rdev->wiphy.bands[params->chandef.chan->band];
int j;
- u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ?
+ u32 flag = params->chandef.chan->band == IEEE80211_BAND_5GHZ ?
IEEE80211_RATE_MANDATORY_A :
IEEE80211_RATE_MANDATORY_B;
@@ -118,11 +118,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
wdev->ibss_fixed = params->channel_fixed;
#ifdef CONFIG_CFG80211_WEXT
- wdev->wext.ibss.channel = params->channel;
+ wdev->wext.ibss.chandef = params->chandef;
#endif
wdev->sme_state = CFG80211_SME_CONNECTING;
- err = cfg80211_can_use_chan(rdev, wdev, params->channel,
+ err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
params->channel_fixed
? CHAN_MODE_SHARED
: CHAN_MODE_EXCLUSIVE);
@@ -251,7 +251,9 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
wdev->wext.ibss.beacon_interval = 100;
/* try to find an IBSS channel if none requested ... */
- if (!wdev->wext.ibss.channel) {
+ if (!wdev->wext.ibss.chandef.chan) {
+ wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
@@ -266,15 +268,15 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
continue;
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
- wdev->wext.ibss.channel = chan;
+ wdev->wext.ibss.chandef.chan = chan;
break;
}
- if (wdev->wext.ibss.channel)
+ if (wdev->wext.ibss.chandef.chan)
break;
}
- if (!wdev->wext.ibss.channel)
+ if (!wdev->wext.ibss.chandef.chan)
return -EINVAL;
}
@@ -336,7 +338,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
return -EINVAL;
}
- if (wdev->wext.ibss.channel == chan)
+ if (wdev->wext.ibss.chandef.chan == chan)
return 0;
wdev_lock(wdev);
@@ -349,7 +351,8 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
return err;
if (chan) {
- wdev->wext.ibss.channel = chan;
+ wdev->wext.ibss.chandef.chan = chan;
+ wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
wdev->wext.ibss.channel_fixed = true;
} else {
/* cfg80211_ibss_wext_join will pick one if needed */
@@ -379,8 +382,8 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
wdev_lock(wdev);
if (wdev->current_bss)
chan = wdev->current_bss->pub.channel;
- else if (wdev->wext.ibss.channel)
- chan = wdev->wext.ibss.channel;
+ else if (wdev->wext.ibss.chandef.chan)
+ chan = wdev->wext.ibss.chandef.chan;
wdev_unlock(wdev);
if (chan) {
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 966cfc4cd79d..3ee5a7282283 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -73,8 +73,6 @@ const struct mesh_config default_mesh_config = {
const struct mesh_setup default_mesh_setup = {
/* cfg80211_join_mesh() will pick a channel if needed */
- .channel = NULL,
- .channel_type = NL80211_CHAN_NO_HT,
.sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
.path_metric = IEEE80211_PATH_METRIC_AIRTIME,
@@ -111,13 +109,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!rdev->ops->join_mesh)
return -EOPNOTSUPP;
- if (!setup->channel) {
+ if (!setup->chandef.chan) {
/* if no channel explicitly given, use preset channel */
- setup->channel = wdev->preset_chan;
- setup->channel_type = wdev->preset_chantype;
+ setup->chandef = wdev->preset_chandef;
}
- if (!setup->channel) {
+ if (!setup->chandef.chan) {
/* if we don't have that either, use the first usable channel */
enum ieee80211_band band;
@@ -137,26 +134,25 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_RADAR))
continue;
- setup->channel = chan;
+ setup->chandef.chan = chan;
break;
}
- if (setup->channel)
+ if (setup->chandef.chan)
break;
}
/* no usable channel ... */
- if (!setup->channel)
+ if (!setup->chandef.chan)
return -EINVAL;
- setup->channel_type = NL80211_CHAN_NO_HT;
+ setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;;
}
- if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel,
- setup->channel_type))
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
return -EINVAL;
- err = cfg80211_can_use_chan(rdev, wdev, setup->channel,
+ err = cfg80211_can_use_chan(rdev, wdev, setup->chandef.chan,
CHAN_MODE_SHARED);
if (err)
return err;
@@ -165,7 +161,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!err) {
memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
wdev->mesh_id_len = setup->mesh_id_len;
- wdev->channel = setup->channel;
+ wdev->channel = setup->chandef.chan;
}
return err;
@@ -188,20 +184,12 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
return err;
}
-int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, int freq,
- enum nl80211_channel_type channel_type)
+int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_channel *channel;
int err;
- channel = rdev_freq_to_chan(rdev, freq, channel_type);
- if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
- channel,
- channel_type)) {
- return -EINVAL;
- }
-
/*
* Workaround for libertas (only!), it puts the interface
* into mesh mode but doesn't implement join_mesh. Instead,
@@ -210,21 +198,21 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
* compatible with 802.11 mesh.
*/
if (rdev->ops->libertas_set_mesh_channel) {
- if (channel_type != NL80211_CHAN_NO_HT)
+ if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
return -EINVAL;
if (!netif_running(wdev->netdev))
return -ENETDOWN;
- err = cfg80211_can_use_chan(rdev, wdev, channel,
+ err = cfg80211_can_use_chan(rdev, wdev, chandef->chan,
CHAN_MODE_SHARED);
if (err)
return err;
err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
- channel);
+ chandef->chan);
if (!err)
- wdev->channel = channel;
+ wdev->channel = chandef->chan;
return err;
}
@@ -232,8 +220,7 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
if (wdev->mesh_id_len)
return -EBUSY;
- wdev->preset_chan = channel;
- wdev->preset_chantype = channel_type;
+ wdev->preset_chandef = *chandef;
return 0;
}
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 4bfd14f7c592..5e8123ee63fd 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -579,31 +579,25 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp)
{
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- trace_cfg80211_ready_on_channel(wdev, cookie, chan, channel_type,
- duration);
- nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type,
- duration, gfp);
+ trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
+ nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, duration, gfp);
}
EXPORT_SYMBOL(cfg80211_ready_on_channel);
void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
gfp_t gfp)
{
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan,
- channel_type);
- nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan,
- channel_type, gfp);
+ trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
+ nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, gfp);
}
EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
@@ -758,10 +752,8 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck,
- bool dont_wait_for_ack, u64 *cookie)
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie)
{
const struct ieee80211_mgmt *mgmt;
u16 stype;
@@ -855,7 +847,6 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
/* Transmit the Action frame as requested by user space */
return rdev_mgmt_tx(rdev, wdev, chan, offchan,
- channel_type, channel_type_valid,
wait, buf, len, no_cck, dont_wait_for_ack,
cookie);
}
@@ -997,15 +988,14 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
-void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
- enum nl80211_channel_type type)
+void cfg80211_ch_switch_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- struct ieee80211_channel *chan;
- trace_cfg80211_ch_switch_notify(dev, freq, type);
+ trace_cfg80211_ch_switch_notify(dev, chandef);
wdev_lock(wdev);
@@ -1013,12 +1003,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
wdev->iftype != NL80211_IFTYPE_P2P_GO))
goto out;
- chan = rdev_freq_to_chan(rdev, freq, type);
- if (WARN_ON(!chan))
- goto out;
-
- wdev->channel = chan;
- nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL);
+ wdev->channel = chandef->chan;
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
out:
wdev_unlock(wdev);
return;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 8c0857815a90..d038fa45ecd1 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -223,8 +223,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = 20-1 },
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
+ [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
+ [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
+
[NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
@@ -1110,6 +1115,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
goto nla_put_failure;
}
CMD(start_p2p_device, START_P2P_DEVICE);
+ CMD(set_mcast_rate, SET_MCAST_RATE);
#ifdef CONFIG_NL80211_TESTMODE
CMD(testmode_cmd, TESTMODE);
@@ -1359,51 +1365,139 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
wdev->iftype == NL80211_IFTYPE_P2P_GO;
}
-static bool nl80211_valid_channel_type(struct genl_info *info,
- enum nl80211_channel_type *channel_type)
+static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
+ struct genl_info *info,
+ struct cfg80211_chan_def *chandef)
{
- enum nl80211_channel_type tmp;
+ struct ieee80211_sta_ht_cap *ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap;
+ u32 control_freq, width;
- if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
- return false;
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ return -EINVAL;
- tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
- if (tmp != NL80211_CHAN_NO_HT &&
- tmp != NL80211_CHAN_HT20 &&
- tmp != NL80211_CHAN_HT40PLUS &&
- tmp != NL80211_CHAN_HT40MINUS)
- return false;
+ control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
- if (channel_type)
- *channel_type = tmp;
+ chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = control_freq;
+ chandef->center_freq2 = 0;
- return true;
+ /* Primary channel not allowed */
+ if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ enum nl80211_channel_type chantype;
+
+ chantype = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+ switch (chantype) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ case NL80211_CHAN_HT40PLUS:
+ case NL80211_CHAN_HT40MINUS:
+ cfg80211_chandef_create(chandef, chandef->chan,
+ chantype);
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
+ chandef->width =
+ nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
+ if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
+ chandef->center_freq1 =
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_CENTER_FREQ1]);
+ if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
+ chandef->center_freq2 =
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_CENTER_FREQ2]);
+ }
+
+ ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap;
+ vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap;
+
+ if (!cfg80211_chan_def_valid(chandef))
+ return -EINVAL;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ if (!ht_cap->ht_supported)
+ return -EINVAL;
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ width = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ /* quick early regulatory check */
+ if (chandef->center_freq1 < control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ return -EINVAL;
+ if (chandef->center_freq1 > control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ return -EINVAL;
+ if (!ht_cap->ht_supported)
+ return -EINVAL;
+ if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+ ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
+ return -EINVAL;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ width = 80;
+ if (!vht_cap->vht_supported)
+ return -EINVAL;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ width = 80;
+ if (!vht_cap->vht_supported)
+ return -EINVAL;
+ if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
+ return -EINVAL;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ width = 160;
+ if (!vht_cap->vht_supported)
+ return -EINVAL;
+ if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1,
+ width, IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+ if (chandef->center_freq2 &&
+ !cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2,
+ width, IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+
+ /* TODO: missing regulatory check on bandwidth */
+
+ return 0;
}
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct genl_info *info)
{
- struct ieee80211_channel *channel;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- u32 freq;
+ struct cfg80211_chan_def chandef;
int result;
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
if (wdev)
iftype = wdev->iftype;
- if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
- return -EINVAL;
-
if (!nl80211_can_set_dev_channel(wdev))
return -EOPNOTSUPP;
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ result = nl80211_parse_chandef(rdev, info, &chandef);
+ if (result)
+ return result;
mutex_lock(&rdev->devlist_mtx);
switch (iftype) {
@@ -1413,22 +1507,18 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
result = -EBUSY;
break;
}
- channel = rdev_freq_to_chan(rdev, freq, channel_type);
- if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
- channel,
- channel_type)) {
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {
result = -EINVAL;
break;
}
- wdev->preset_chan = channel;
- wdev->preset_chantype = channel_type;
+ wdev->preset_chandef = chandef;
result = 0;
break;
case NL80211_IFTYPE_MESH_POINT:
- result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type);
+ result = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
break;
case NL80211_IFTYPE_MONITOR:
- result = cfg80211_set_monitor_channel(rdev, freq, channel_type);
+ result = cfg80211_set_monitor_channel(rdev, &chandef);
break;
default:
result = -EINVAL;
@@ -1516,10 +1606,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
result = 0;
mutex_lock(&rdev->mtx);
- } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
+ } else
wdev = netdev->ieee80211_ptr;
- else
- wdev = NULL;
/*
* end workaround code, by now the rdev is available
@@ -1579,15 +1667,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- result = __nl80211_set_channel(rdev, wdev, info);
+ result = __nl80211_set_channel(rdev,
+ nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
+ info);
if (result)
goto bad_res;
}
if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
+ struct wireless_dev *txp_wdev = wdev;
enum nl80211_tx_power_setting type;
int idx, mbm = 0;
+ if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
+ txp_wdev = NULL;
+
if (!rdev->ops->set_tx_power) {
result = -EOPNOTSUPP;
goto bad_res;
@@ -1607,7 +1701,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
mbm = nla_get_u32(info->attrs[idx]);
}
- result = rdev_set_tx_power(rdev, type, mbm);
+ result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
if (result)
goto bad_res;
}
@@ -1744,6 +1838,35 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32);
}
+static int nl80211_send_chandef(struct sk_buff *msg,
+ struct cfg80211_chan_def *chandef)
+{
+ WARN_ON(!cfg80211_chan_def_valid(chandef));
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ chandef->chan->center_freq))
+ return -ENOBUFS;
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ cfg80211_get_chandef_type(chandef)))
+ return -ENOBUFS;
+ break;
+ default:
+ break;
+ }
+ if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
+ return -ENOBUFS;
+ if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
+ return -ENOBUFS;
+ if (chandef->center_freq2 &&
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
+ return -ENOBUFS;
+ return 0;
+}
+
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
@@ -1770,15 +1893,18 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
goto nla_put_failure;
if (rdev->ops->get_channel) {
- struct ieee80211_channel *chan;
- enum nl80211_channel_type channel_type;
-
- chan = rdev_get_channel(rdev, wdev, &channel_type);
- if (chan &&
- (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
- chan->center_freq) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
- channel_type)))
+ int ret;
+ struct cfg80211_chan_def chandef;
+
+ ret = rdev_get_channel(rdev, wdev, &chandef);
+ if (ret == 0) {
+ if (nl80211_send_chandef(msg, &chandef))
+ goto nla_put_failure;
+ }
+ }
+
+ if (wdev->ssid_len) {
+ if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
goto nla_put_failure;
}
@@ -2482,11 +2608,10 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
wdev->iftype != NL80211_IFTYPE_P2P_GO)
continue;
- if (!wdev->preset_chan)
+ if (!wdev->preset_chandef.chan)
continue;
- params->channel = wdev->preset_chan;
- params->channel_type = wdev->preset_chantype;
+ params->chandef = wdev->preset_chandef;
ret = true;
break;
}
@@ -2608,30 +2733,19 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- params.channel = rdev_freq_to_chan(rdev,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
- channel_type);
- if (!params.channel)
- return -EINVAL;
- params.channel_type = channel_type;
- } else if (wdev->preset_chan) {
- params.channel = wdev->preset_chan;
- params.channel_type = wdev->preset_chantype;
+ err = nl80211_parse_chandef(rdev, info, &params.chandef);
+ if (err)
+ return err;
+ } else if (wdev->preset_chandef.chan) {
+ params.chandef = wdev->preset_chandef;
} else if (!nl80211_get_ap_channel(rdev, &params))
return -EINVAL;
- if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel,
- params.channel_type))
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
return -EINVAL;
mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_can_use_chan(rdev, wdev, params.channel,
+ err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan,
CHAN_MODE_SHARED);
mutex_unlock(&rdev->devlist_mtx);
@@ -2640,10 +2754,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
err = rdev_start_ap(rdev, dev, &params);
if (!err) {
- wdev->preset_chan = params.channel;
- wdev->preset_chantype = params.channel_type;
+ wdev->preset_chandef = params.chandef;
wdev->beacon_interval = params.beacon_interval;
- wdev->channel = params.channel;
+ wdev->channel = params.chandef.chan;
+ wdev->ssid_len = params.ssid_len;
+ memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
}
return err;
}
@@ -2775,29 +2890,52 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
rate = nla_nest_start(msg, attr);
if (!rate)
- goto nla_put_failure;
+ return false;
/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
bitrate = cfg80211_calculate_bitrate(info);
/* report 16-bit bitrate only if we can */
bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
- if ((bitrate > 0 &&
- nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) ||
- (bitrate_compat > 0 &&
- nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) ||
- ((info->flags & RATE_INFO_FLAGS_MCS) &&
- nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) ||
- ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) &&
- nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) ||
- ((info->flags & RATE_INFO_FLAGS_SHORT_GI) &&
- nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)))
- goto nla_put_failure;
+ if (bitrate > 0 &&
+ nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate))
+ return false;
+ if (bitrate_compat > 0 &&
+ nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
+ return false;
+
+ if (info->flags & RATE_INFO_FLAGS_MCS) {
+ if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+ nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+ return false;
+ } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
+ if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
+ return false;
+ if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+ nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+ return false;
+ }
nla_nest_end(msg, rate);
return true;
-
-nla_put_failure:
- return false;
}
static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
@@ -5318,8 +5456,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
return -EINVAL;
- if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
- !info->attrs[NL80211_ATTR_SSID] ||
+ if (!info->attrs[NL80211_ATTR_SSID] ||
!nla_len(info->attrs[NL80211_ATTR_SSID]))
return -EINVAL;
@@ -5354,35 +5491,17 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
- enum nl80211_channel_type channel_type;
-
- if (!nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- if (channel_type != NL80211_CHAN_NO_HT &&
- !(wiphy->features & NL80211_FEATURE_HT_IBSS))
- return -EINVAL;
-
- ibss.channel_type = channel_type;
- } else {
- ibss.channel_type = NL80211_CHAN_NO_HT;
- }
+ err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
+ if (err)
+ return err;
- ibss.channel = rdev_freq_to_chan(rdev,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
- ibss.channel_type);
- if (!ibss.channel ||
- ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
- ibss.channel->flags & IEEE80211_CHAN_DISABLED)
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
return -EINVAL;
- /* Both channels should be able to initiate communication */
- if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
- ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
- !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
- ibss.channel_type))
+ if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
return -EINVAL;
+ if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
@@ -5393,7 +5512,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
int n_rates =
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
struct ieee80211_supported_band *sband =
- wiphy->bands[ibss.channel->band];
+ wiphy->bands[ibss.chandef.chan->band];
err = ieee80211_get_ratemask(sband, rates, n_rates,
&ibss.basic_rates);
@@ -5415,7 +5534,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (IS_ERR(connkeys))
return PTR_ERR(connkeys);
- if ((ibss.channel_type != NL80211_CHAN_NO_HT) && no_ht) {
+ if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
+ no_ht) {
kfree(connkeys);
return -EINVAL;
}
@@ -5444,6 +5564,36 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
return cfg80211_leave_ibss(rdev, dev, false);
}
+static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ int mcast_rate[IEEE80211_NUM_BANDS];
+ u32 nla_rate;
+ int err;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->set_mcast_rate)
+ return -EOPNOTSUPP;
+
+ memset(mcast_rate, 0, sizeof(mcast_rate));
+
+ if (!info->attrs[NL80211_ATTR_MCAST_RATE])
+ return -EINVAL;
+
+ nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
+ if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
+ return -EINVAL;
+
+ err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+
+ return err;
+}
+
+
#ifdef CONFIG_NL80211_TESTMODE
static struct genl_multicast_group nl80211_testmode_mcgrp = {
.name = "testmode",
@@ -5906,12 +6056,11 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev = info->user_ptr[1];
- struct ieee80211_channel *chan;
+ struct cfg80211_chan_def chandef;
struct sk_buff *msg;
void *hdr;
u64 cookie;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- u32 freq, duration;
+ u32 duration;
int err;
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
@@ -5932,14 +6081,9 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
duration > rdev->wiphy.max_remain_on_channel_duration)
return -EINVAL;
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
- chan = rdev_freq_to_chan(rdev, freq, channel_type);
- if (chan == NULL)
- return -EINVAL;
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
@@ -5953,8 +6097,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
goto free_msg;
}
- err = rdev_remain_on_channel(rdev, wdev, chan, channel_type, duration,
- &cookie);
+ err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
+ duration, &cookie);
if (err)
goto free_msg;
@@ -6173,10 +6317,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev = info->user_ptr[1];
- struct ieee80211_channel *chan;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- bool channel_type_valid = false;
- u32 freq;
+ struct cfg80211_chan_def chandef;
int err;
void *hdr = NULL;
u64 cookie;
@@ -6186,8 +6327,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
- if (!info->attrs[NL80211_ATTR_FRAME] ||
- !info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ if (!info->attrs[NL80211_ATTR_FRAME])
return -EINVAL;
if (!rdev->ops->mgmt_tx)
@@ -6222,12 +6362,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
}
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
- if (!nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
- channel_type_valid = true;
- }
-
offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
@@ -6235,10 +6369,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
- freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
- chan = rdev_freq_to_chan(rdev, freq, channel_type);
- if (chan == NULL)
- return -EINVAL;
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
if (!dont_wait_for_ack) {
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -6254,8 +6387,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
}
}
- err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type,
- channel_type_valid, wait,
+ err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait,
nla_data(info->attrs[NL80211_ATTR_FRAME]),
nla_len(info->attrs[NL80211_ATTR_FRAME]),
no_cck, dont_wait_for_ack, &cookie);
@@ -6519,21 +6651,12 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- setup.channel = rdev_freq_to_chan(rdev,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
- channel_type);
- if (!setup.channel)
- return -EINVAL;
- setup.channel_type = channel_type;
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ if (err)
+ return err;
} else {
/* cfg80211_join_mesh() will sort it out */
- setup.channel = NULL;
+ setup.chandef.chan = NULL;
}
return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
@@ -6899,16 +7022,35 @@ static int nl80211_probe_client(struct sk_buff *skb,
static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_beacon_registration *reg, *nreg;
+ int rv;
if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
return -EOPNOTSUPP;
- if (rdev->ap_beacons_nlportid)
- return -EBUSY;
+ nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
+ if (!nreg)
+ return -ENOMEM;
- rdev->ap_beacons_nlportid = info->snd_portid;
+ /* First, check if already registered. */
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+ if (reg->nlportid == info->snd_portid) {
+ rv = -EALREADY;
+ goto out_err;
+ }
+ }
+ /* Add it to the list */
+ nreg->nlportid = info->snd_portid;
+ list_add(&nreg->list, &rdev->beacon_registrations);
+
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
return 0;
+out_err:
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ kfree(nreg);
+ return rv;
}
static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
@@ -7625,6 +7767,14 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_SET_MCAST_RATE,
+ .doit = nl80211_set_mcast_rate,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -8326,7 +8476,6 @@ static void nl80211_send_remain_on_chan_event(
int cmd, struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp)
{
struct sk_buff *msg;
@@ -8347,7 +8496,8 @@ static void nl80211_send_remain_on_chan_event(
wdev->netdev->ifindex)) ||
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_NO_HT) ||
nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
goto nla_put_failure;
@@ -8369,23 +8519,20 @@ static void nl80211_send_remain_on_chan_event(
void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp)
{
nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
rdev, wdev, cookie, chan,
- channel_type, duration, gfp);
+ duration, gfp);
}
void nl80211_send_remain_on_channel_cancel(
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- u64 cookie, struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type, gfp_t gfp)
+ u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
{
nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
- rdev, wdev, cookie, chan,
- channel_type, 0, gfp);
+ rdev, wdev, cookie, chan, 0, gfp);
}
void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
@@ -8741,8 +8888,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
}
void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, int freq,
- enum nl80211_channel_type type, gfp_t gfp)
+ struct net_device *netdev,
+ struct cfg80211_chan_def *chandef, gfp_t gfp)
{
struct sk_buff *msg;
void *hdr;
@@ -8757,9 +8904,10 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
return;
}
- if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type))
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+ goto nla_put_failure;
+
+ if (nl80211_send_chandef(msg, chandef))
goto nla_put_failure;
genlmsg_end(msg, hdr);
@@ -8914,46 +9062,96 @@ EXPORT_SYMBOL(cfg80211_probe_status);
void cfg80211_report_obss_beacon(struct wiphy *wiphy,
const u8 *frame, size_t len,
- int freq, int sig_dbm, gfp_t gfp)
+ int freq, int sig_dbm)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg;
void *hdr;
- u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid);
+ struct cfg80211_beacon_registration *reg;
trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
- if (!nlportid)
- return;
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+ msg = nlmsg_new(len + 100, GFP_ATOMIC);
+ if (!msg) {
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ return;
+ }
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (freq &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+ (sig_dbm &&
+ nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, frame))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
- msg = nlmsg_new(len + 100, gfp);
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
+ }
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ return;
+
+ nla_put_failure:
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ if (hdr)
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_report_obss_beacon);
+
+void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
+ enum nl80211_tdls_operation oper,
+ u16 reason_code, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+
+ trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
+ reason_code);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
if (!hdr) {
nlmsg_free(msg);
return;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- (freq &&
- nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
- (sig_dbm &&
- nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
- nla_put(msg, NL80211_ATTR_FRAME, len, frame))
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
+ (reason_code > 0 &&
+ nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
goto nla_put_failure;
- genlmsg_end(msg, hdr);
+ err = genlmsg_end(msg, hdr);
+ if (err < 0) {
+ nlmsg_free(msg);
+ return;
+ }
- genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
+ 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);
}
-EXPORT_SYMBOL(cfg80211_report_obss_beacon);
+EXPORT_SYMBOL(cfg80211_tdls_oper_request);
static int nl80211_netlink_notify(struct notifier_block * nb,
unsigned long state,
@@ -8962,6 +9160,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
struct netlink_notify *notify = _notify;
struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;
+ struct cfg80211_beacon_registration *reg, *tmp;
if (state != NETLINK_URELEASE)
return NOTIFY_DONE;
@@ -8971,8 +9170,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
cfg80211_mlme_unregister_socket(wdev, notify->portid);
- if (rdev->ap_beacons_nlportid == notify->portid)
- rdev->ap_beacons_nlportid = 0;
+
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
+ list) {
+ if (reg->nlportid == notify->portid) {
+ list_del(&reg->list);
+ kfree(reg);
+ break;
+ }
+ }
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
}
rcu_read_unlock();
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index f6153516068c..2acba8477e9d 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -76,13 +76,11 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp);
void nl80211_send_remain_on_channel_cancel(
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- u64 cookie, struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type, gfp_t gfp);
+ u64 cookie, struct ieee80211_channel *chan, gfp_t gfp);
void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *mac_addr,
@@ -129,8 +127,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
const u8 *bssid, bool preauth, gfp_t gfp);
void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
- struct net_device *dev, int freq,
- enum nl80211_channel_type type, gfp_t gfp);
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef, gfp_t gfp);
bool nl80211_unexpected_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index eb5f8974e148..6c0c8191f837 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -359,12 +359,11 @@ rdev_libertas_set_mesh_channel(struct cfg80211_registered_device *rdev,
static inline int
rdev_set_monitor_channel(struct cfg80211_registered_device *rdev,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+ struct cfg80211_chan_def *chandef)
{
int ret;
- trace_rdev_set_monitor_channel(&rdev->wiphy, chan, channel_type);
- ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chan, channel_type);
+ trace_rdev_set_monitor_channel(&rdev->wiphy, chandef);
+ ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
@@ -476,21 +475,22 @@ rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed)
}
static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, int mbm)
{
int ret;
- trace_rdev_set_tx_power(&rdev->wiphy, type, mbm);
- ret = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
+ trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm);
+ ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev,
- int *dbm)
+ struct wireless_dev *wdev, int *dbm)
{
int ret;
- trace_rdev_get_tx_power(&rdev->wiphy);
- ret = rdev->ops->get_tx_power(&rdev->wiphy, dbm);
+ trace_rdev_get_tx_power(&rdev->wiphy, wdev);
+ ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm);
trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm);
return ret;
}
@@ -599,14 +599,12 @@ static inline int
rdev_remain_on_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, u64 *cookie)
{
int ret;
- trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, channel_type,
- duration);
+ trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration);
ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan,
- channel_type, duration, cookie);
+ duration, cookie);
trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
return ret;
}
@@ -625,17 +623,15 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev,
static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck,
- bool dont_wait_for_ack, u64 *cookie)
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie)
{
int ret;
- trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, channel_type,
- channel_type_valid, wait, no_cck, dont_wait_for_ack);
+ trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
+ wait, no_cck, dont_wait_for_ack);
ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
- channel_type, channel_type_valid, wait, buf,
- len, no_cck, dont_wait_for_ack, cookie);
+ wait, buf, len, no_cck,
+ dont_wait_for_ack, cookie);
trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
return ret;
}
@@ -847,14 +843,17 @@ static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev,
trace_rdev_return_void(&rdev->wiphy);
}
-static inline struct ieee80211_channel
-*rdev_get_channel(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, enum nl80211_channel_type *type)
+static inline int
+rdev_get_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_channel *ret;
+ int ret;
+
trace_rdev_get_channel(&rdev->wiphy, wdev);
- ret = rdev->ops->get_channel(&rdev->wiphy, wdev, type);
- trace_rdev_return_channel(&rdev->wiphy, ret, *type);
+ ret = rdev->ops->get_channel(&rdev->wiphy, wdev, chandef);
+ trace_rdev_return_chandef(&rdev->wiphy, ret, chandef);
+
return ret;
}
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 7f97a087f452..9596015975d2 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -771,6 +771,38 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
return found;
}
+static struct ieee80211_channel *
+cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
+ struct ieee80211_channel *channel)
+{
+ const u8 *tmp;
+ u32 freq;
+ int channel_number = -1;
+
+ tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen);
+ if (tmp && tmp[1] == 1) {
+ channel_number = tmp[2];
+ } else {
+ tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
+ if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
+ struct ieee80211_ht_operation *htop = (void *)(tmp + 2);
+
+ channel_number = htop->primary_chan;
+ }
+ }
+
+ if (channel_number < 0)
+ return channel;
+
+ freq = ieee80211_channel_to_frequency(channel_number, channel->band);
+ channel = ieee80211_get_channel(wiphy, freq);
+ if (!channel)
+ return NULL;
+ if (channel->flags & IEEE80211_CHAN_DISABLED)
+ return NULL;
+ return channel;
+}
+
struct cfg80211_bss*
cfg80211_inform_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel,
@@ -790,6 +822,10 @@ cfg80211_inform_bss(struct wiphy *wiphy,
(signal < 0 || signal > 100)))
return NULL;
+ channel = cfg80211_get_bss_channel(wiphy, ie, ielen, channel);
+ if (!channel)
+ return NULL;
+
res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
if (!res)
return NULL;
@@ -839,11 +875,13 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
s32 signal, gfp_t gfp)
{
struct cfg80211_internal_bss *res;
-
size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
size_t privsz;
+ BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
+ offsetof(struct ieee80211_mgmt, u.beacon.variable));
+
trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);
if (WARN_ON(!mgmt))
@@ -861,6 +899,11 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
privsz = wiphy->bss_priv_size;
+ channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
+ ielen, channel);
+ if (!channel)
+ return NULL;
+
res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
if (!res)
return NULL;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 0ca71caf85fb..2134576f426e 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -20,29 +20,26 @@
#define MAC_PR_FMT "%pM"
#define MAC_PR_ARG(entry_mac) (__entry->entry_mac)
-#define WIPHY_ENTRY MAC_ENTRY(wiphy_mac)
-#define WIPHY_ASSIGN MAC_ASSIGN(wiphy_mac, wiphy->perm_addr)
-#define WIPHY_PR_FMT "wiphy " MAC_PR_FMT
-#define WIPHY_PR_ARG MAC_PR_ARG(wiphy_mac)
-
-#define WDEV_ENTRY __field(u32, id)
-#define WDEV_ASSIGN (__entry->id) = (wdev->identifier)
-#define WDEV_PR_FMT ", wdev id: %u"
-#define WDEV_PR_ARG (__entry->id)
-
-#define NETDEV_ENTRY __array(char, name, IFNAMSIZ) \
- MAC_ENTRY(netdev_addr) \
- __field(int, ifindex)
+#define MAXNAME 32
+#define WIPHY_ENTRY __array(char, wiphy_name, 32)
+#define WIPHY_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(wiphy), MAXNAME)
+#define WIPHY_PR_FMT "%s"
+#define WIPHY_PR_ARG __entry->wiphy_name
+
+#define WDEV_ENTRY __field(u32, id)
+#define WDEV_ASSIGN (__entry->id) = (wdev ? wdev->identifier : 0)
+#define WDEV_PR_FMT "wdev(%u)"
+#define WDEV_PR_ARG (__entry->id)
+
+#define NETDEV_ENTRY __array(char, name, IFNAMSIZ) \
+ __field(int, ifindex)
#define NETDEV_ASSIGN \
do { \
memcpy(__entry->name, netdev->name, IFNAMSIZ); \
- MAC_ASSIGN(netdev_addr, netdev->dev_addr); \
(__entry->ifindex) = (netdev->ifindex); \
} while (0)
-#define NETDEV_PR_FMT ", netdev - name: %s, addr: " MAC_PR_FMT \
- ", intf index: %d"
-#define NETDEV_PR_ARG (__entry->name), MAC_PR_ARG(netdev_addr), \
- (__entry->ifindex)
+#define NETDEV_PR_FMT "netdev:%s(%d)"
+#define NETDEV_PR_ARG __entry->name, __entry->ifindex
#define MESH_CFG_ENTRY __field(u16, dot11MeshRetryTimeout) \
__field(u16, dot11MeshConfirmTimeout) \
@@ -123,9 +120,37 @@
__entry->center_freq = 0; \
} \
} while (0)
-#define CHAN_PR_FMT ", band: %d, freq: %u"
+#define CHAN_PR_FMT "band: %d, freq: %u"
#define CHAN_PR_ARG __entry->band, __entry->center_freq
+#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band) \
+ __field(u32, control_freq) \
+ __field(u32, width) \
+ __field(u32, center_freq1) \
+ __field(u32, center_freq2)
+#define CHAN_DEF_ASSIGN(chandef) \
+ do { \
+ if ((chandef) && (chandef)->chan) { \
+ __entry->band = (chandef)->chan->band; \
+ __entry->control_freq = \
+ (chandef)->chan->center_freq; \
+ __entry->width = (chandef)->width; \
+ __entry->center_freq1 = (chandef)->center_freq1;\
+ __entry->center_freq2 = (chandef)->center_freq2;\
+ } else { \
+ __entry->band = 0; \
+ __entry->control_freq = 0; \
+ __entry->width = 0; \
+ __entry->center_freq1 = 0; \
+ __entry->center_freq2 = 0; \
+ } \
+ } while (0)
+#define CHAN_DEF_PR_FMT \
+ "band: %d, control freq: %u, width: %d, cf1: %u, cf2: %u"
+#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \
+ __entry->width, __entry->center_freq1, \
+ __entry->center_freq2
+
#define SINFO_ENTRY __field(int, generation) \
__field(u32, connected_time) \
__field(u32, inactive_time) \
@@ -260,11 +285,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna,
TP_ARGS(wiphy)
);
-DEFINE_EVENT(wiphy_only_evt, rdev_get_tx_power,
- TP_PROTO(struct wiphy *wiphy),
- TP_ARGS(wiphy)
-);
-
DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll,
TP_PROTO(struct wiphy *wiphy),
TP_ARGS(wiphy)
@@ -318,7 +338,7 @@ DECLARE_EVENT_CLASS(wiphy_wdev_evt,
WIPHY_ASSIGN;
WDEV_ASSIGN;
),
- TP_printk(WIPHY_PR_FMT WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
);
DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev,
@@ -345,7 +365,7 @@ TRACE_EVENT(rdev_change_virtual_intf,
NETDEV_ASSIGN;
__entry->type = type;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", type: %d",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", type: %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->type)
);
@@ -367,7 +387,7 @@ DECLARE_EVENT_CLASS(key_handle,
__entry->key_index = key_index;
__entry->pairwise = pairwise;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
BOOL_TO_STR(__entry->pairwise), MAC_PR_ARG(mac_addr))
);
@@ -408,7 +428,7 @@ TRACE_EVENT(rdev_set_default_key,
__entry->unicast = unicast;
__entry->multicast = multicast;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
BOOL_TO_STR(__entry->unicast),
BOOL_TO_STR(__entry->multicast))
@@ -427,7 +447,7 @@ TRACE_EVENT(rdev_set_default_mgmt_key,
NETDEV_ASSIGN;
__entry->key_index = key_index;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key index: %u",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index)
);
@@ -438,7 +458,7 @@ TRACE_EVENT(rdev_start_ap,
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
- CHAN_ENTRY
+ CHAN_DEF_ENTRY
__field(int, beacon_interval)
__field(int, dtim_period)
__array(char, ssid, IEEE80211_MAX_SSID_LEN + 1)
@@ -451,7 +471,7 @@ TRACE_EVENT(rdev_start_ap,
TP_fast_assign(
WIPHY_ASSIGN;
NETDEV_ASSIGN;
- CHAN_ASSIGN(settings->channel);
+ CHAN_DEF_ASSIGN(&settings->chandef);
__entry->beacon_interval = settings->beacon_interval;
__entry->dtim_period = settings->dtim_period;
__entry->hidden_ssid = settings->hidden_ssid;
@@ -462,11 +482,11 @@ TRACE_EVENT(rdev_start_ap,
memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
memcpy(__entry->ssid, settings->ssid, settings->ssid_len);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", AP settings - ssid: %s, "
- CHAN_PR_FMT ", beacon interval: %d, dtim period: %d, "
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", AP settings - ssid: %s, "
+ CHAN_DEF_PR_FMT ", beacon interval: %d, dtim period: %d, "
"hidden ssid: %d, wpa versions: %u, privacy: %s, "
"auth type: %d, inactivity timeout: %d",
- WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_PR_ARG,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_DEF_PR_ARG,
__entry->beacon_interval, __entry->dtim_period,
__entry->hidden_ssid, __entry->wpa_ver,
BOOL_TO_STR(__entry->privacy), __entry->auth_type,
@@ -515,7 +535,7 @@ TRACE_EVENT(rdev_change_beacon,
info->probe_resp, info->probe_resp_len);
}
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
);
DECLARE_EVENT_CLASS(wiphy_netdev_evt,
@@ -529,7 +549,7 @@ DECLARE_EVENT_CLASS(wiphy_netdev_evt,
WIPHY_ASSIGN;
NETDEV_ASSIGN;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
);
DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap,
@@ -607,7 +627,7 @@ DECLARE_EVENT_CLASS(station_add_change,
memcpy(__entry->ht_capa, params->ht_capa,
sizeof(struct ieee80211_ht_cap));
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
", station flags mask: %u, station flags set: %u, "
"station modify mask: %u, listen interval: %d, aid: %u, "
"plink action: %u, plink state: %u, uapsd queues: %u",
@@ -643,7 +663,7 @@ DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt,
NETDEV_ASSIGN;
MAC_ASSIGN(sta_mac, mac);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", mac: " MAC_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mac: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac))
);
@@ -683,7 +703,7 @@ TRACE_EVENT(rdev_dump_station,
MAC_ASSIGN(sta_mac, mac);
__entry->idx = idx;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
__entry->idx)
);
@@ -721,7 +741,7 @@ DECLARE_EVENT_CLASS(mpath_evt,
MAC_ASSIGN(dst, dst);
MAC_ASSIGN(next_hop, next_hop);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dst),
MAC_PR_ARG(next_hop))
);
@@ -762,7 +782,7 @@ TRACE_EVENT(rdev_dump_mpath,
MAC_ASSIGN(next_hop, next_hop);
__entry->idx = idx;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", index: %d, destination: "
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
MAC_PR_FMT ", next hop: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst),
MAC_PR_ARG(next_hop))
@@ -839,7 +859,7 @@ TRACE_EVENT(rdev_update_mesh_config,
MESH_CFG_ASSIGN;
__entry->mask = mask;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", mask: %u",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mask: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->mask)
);
@@ -858,7 +878,7 @@ TRACE_EVENT(rdev_join_mesh,
NETDEV_ASSIGN;
MESH_CFG_ASSIGN;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG)
);
@@ -884,7 +904,7 @@ TRACE_EVENT(rdev_change_bss,
__entry->ap_isolate = params->ap_isolate;
__entry->ht_opmode = params->ht_opmode;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", use cts prot: %d, "
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", use cts prot: %d, "
"use short preamble: %d, use short slot time: %d, "
"ap isolate: %d, ht opmode: %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->use_cts_prot,
@@ -914,7 +934,7 @@ TRACE_EVENT(rdev_set_txq_params,
__entry->cwmax = params->cwmax;
__entry->aifs = params->aifs;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", ac: %d, txop: %u, cwmin: %u, cwmax: %u, aifs: %u",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", ac: %d, txop: %u, cwmin: %u, cwmax: %u, aifs: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ac, __entry->txop,
__entry->cwmin, __entry->cwmax, __entry->aifs)
);
@@ -933,26 +953,24 @@ TRACE_EVENT(rdev_libertas_set_mesh_channel,
NETDEV_ASSIGN;
CHAN_ASSIGN(chan);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT CHAN_PR_FMT, WIPHY_PR_ARG,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_PR_FMT, WIPHY_PR_ARG,
NETDEV_PR_ARG, CHAN_PR_ARG)
);
TRACE_EVENT(rdev_set_monitor_channel,
- TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *chan,
- enum nl80211_channel_type chan_type),
- TP_ARGS(wiphy, chan, chan_type),
+ TP_PROTO(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, chandef),
TP_STRUCT__entry(
WIPHY_ENTRY
- CHAN_ENTRY
- __field(enum nl80211_channel_type, chan_type)
+ CHAN_DEF_ENTRY
),
TP_fast_assign(
WIPHY_ASSIGN;
- CHAN_ASSIGN(chan);
- __entry->chan_type = chan_type;
+ CHAN_DEF_ASSIGN(chandef);
),
- TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel type : %d",
- WIPHY_PR_ARG, CHAN_PR_ARG, __entry->chan_type)
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
);
TRACE_EVENT(rdev_auth,
@@ -974,7 +992,7 @@ TRACE_EVENT(rdev_auth,
memset(__entry->bssid, 0, ETH_ALEN);
__entry->auth_type = req->auth_type;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->auth_type,
MAC_PR_ARG(bssid))
);
@@ -1002,7 +1020,7 @@ TRACE_EVENT(rdev_assoc,
__entry->use_mfp = req->use_mfp;
__entry->flags = req->flags;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
", previous bssid: " MAC_PR_FMT ", use mfp: %s, flags: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
MAC_PR_ARG(prev_bssid), BOOL_TO_STR(__entry->use_mfp),
@@ -1025,7 +1043,7 @@ TRACE_EVENT(rdev_deauth,
MAC_ASSIGN(bssid, req->bssid);
__entry->reason_code = req->reason_code;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
__entry->reason_code)
);
@@ -1051,7 +1069,7 @@ TRACE_EVENT(rdev_disassoc,
__entry->reason_code = req->reason_code;
__entry->local_state_change = req->local_state_change;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
", reason: %u, local state change: %s",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
__entry->reason_code,
@@ -1072,7 +1090,7 @@ TRACE_EVENT(rdev_mgmt_tx_cancel_wait,
WDEV_ASSIGN;
__entry->cookie = cookie;
),
- TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", cookie: %llu ",
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu ",
WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
);
@@ -1092,7 +1110,7 @@ TRACE_EVENT(rdev_set_power_mgmt,
__entry->enabled = enabled;
__entry->timeout = timeout;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", %senabled, timeout: %d ",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", %senabled, timeout: %d ",
WIPHY_PR_ARG, NETDEV_PR_ARG,
__entry->enabled ? "" : "not ", __entry->timeout)
);
@@ -1122,7 +1140,7 @@ TRACE_EVENT(rdev_connect,
__entry->wpa_versions = sme->crypto.wpa_versions;
__entry->flags = sme->flags;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, "
"flags: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid,
@@ -1147,7 +1165,7 @@ TRACE_EVENT(rdev_set_cqm_rssi_config,
__entry->rssi_thold = rssi_thold;
__entry->rssi_hyst = rssi_hyst;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
", rssi_thold: %d, rssi_hyst: %u ",
WIPHY_PR_ARG, NETDEV_PR_ARG,
__entry->rssi_thold, __entry->rssi_hyst)
@@ -1171,7 +1189,7 @@ TRACE_EVENT(rdev_set_cqm_txe_config,
__entry->pkts = pkts;
__entry->intvl = intvl;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", rate: %u, packets: %u, interval: %u",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", rate: %u, packets: %u, interval: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->rate, __entry->pkts,
__entry->intvl)
);
@@ -1190,7 +1208,7 @@ TRACE_EVENT(rdev_disconnect,
NETDEV_ASSIGN;
__entry->reason_code = reason_code;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", reason code: %u", WIPHY_PR_ARG,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", reason code: %u", WIPHY_PR_ARG,
NETDEV_PR_ARG, __entry->reason_code)
);
@@ -1211,7 +1229,7 @@ TRACE_EVENT(rdev_join_ibss,
memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
memcpy(__entry->ssid, params->ssid, params->ssid_len);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
);
@@ -1230,22 +1248,29 @@ TRACE_EVENT(rdev_set_wiphy_params,
WIPHY_PR_ARG, __entry->changed)
);
+DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
TRACE_EVENT(rdev_set_tx_power,
- TP_PROTO(struct wiphy *wiphy, enum nl80211_tx_power_setting type,
- int mbm),
- TP_ARGS(wiphy, type, mbm),
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm),
+ TP_ARGS(wiphy, wdev, type, mbm),
TP_STRUCT__entry(
WIPHY_ENTRY
+ WDEV_ENTRY
__field(enum nl80211_tx_power_setting, type)
__field(int, mbm)
),
TP_fast_assign(
WIPHY_ASSIGN;
+ WDEV_ASSIGN;
__entry->type = type;
__entry->mbm = mbm;
),
- TP_printk(WIPHY_PR_FMT ", type: %d, mbm: %d",
- WIPHY_PR_ARG, __entry->type, __entry->mbm)
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type: %u, mbm: %d",
+ WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm)
);
TRACE_EVENT(rdev_return_int_int,
@@ -1305,7 +1330,7 @@ TRACE_EVENT(rdev_set_bitrate_mask,
NETDEV_ASSIGN;
MAC_ASSIGN(peer, peer);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", peer: " MAC_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
);
@@ -1325,7 +1350,7 @@ TRACE_EVENT(rdev_mgmt_frame_register,
__entry->frame_type = frame_type;
__entry->reg = reg;
),
- TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", frame_type: %u, reg: %s ",
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", frame_type: 0x%.2x, reg: %s ",
WIPHY_PR_ARG, WDEV_PR_ARG, __entry->frame_type,
__entry->reg ? "true" : "false")
);
@@ -1411,7 +1436,7 @@ TRACE_EVENT(rdev_sched_scan_start,
WIPHY_ASSIGN;
NETDEV_ASSIGN;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG)
);
@@ -1439,7 +1464,7 @@ TRACE_EVENT(rdev_tdls_mgmt,
__entry->status_code = status_code;
memcpy(__get_dynamic_array(buf), buf, len);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT ", action_code: %u, "
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
"dialog_token: %u, status_code: %u, buf: %#.2x ",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
__entry->action_code, __entry->dialog_token,
@@ -1459,7 +1484,7 @@ TRACE_EVENT(rdev_dump_survey,
NETDEV_ASSIGN;
__entry->idx = idx;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", index: %d",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx)
);
@@ -1516,7 +1541,7 @@ TRACE_EVENT(rdev_tdls_oper,
MAC_ASSIGN(peer, peer);
__entry->oper = oper;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT ", oper: %d",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", oper: %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper)
);
@@ -1534,7 +1559,7 @@ DECLARE_EVENT_CLASS(rdev_pmksa,
NETDEV_ASSIGN;
MAC_ASSIGN(bssid, pmksa->bssid);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid))
);
@@ -1552,7 +1577,7 @@ TRACE_EVENT(rdev_probe_client,
NETDEV_ASSIGN;
MAC_ASSIGN(peer, peer);
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT,
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
);
@@ -1571,25 +1596,22 @@ DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa,
TRACE_EVENT(rdev_remain_on_channel,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type, unsigned int duration),
- TP_ARGS(wiphy, wdev, chan, channel_type, duration),
+ unsigned int duration),
+ TP_ARGS(wiphy, wdev, chan, duration),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
CHAN_ENTRY
- __field(enum nl80211_channel_type, channel_type)
__field(unsigned int, duration)
),
TP_fast_assign(
WIPHY_ASSIGN;
WDEV_ASSIGN;
CHAN_ASSIGN(chan);
- __entry->channel_type = channel_type;
__entry->duration = duration;
),
- TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", channel type: %d, duration: %u",
- WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->channel_type,
- __entry->duration)
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", duration: %u",
+ WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration)
);
TRACE_EVENT(rdev_return_int_cookie,
@@ -1622,25 +1644,20 @@ TRACE_EVENT(rdev_cancel_remain_on_channel,
WDEV_ASSIGN;
__entry->cookie = cookie;
),
- TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", cookie: %llu",
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu",
WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
);
TRACE_EVENT(rdev_mgmt_tx,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait, bool no_cck,
- bool dont_wait_for_ack),
- TP_ARGS(wiphy, wdev, chan, offchan, channel_type, channel_type_valid,
- wait, no_cck, dont_wait_for_ack),
+ unsigned int wait, bool no_cck, bool dont_wait_for_ack),
+ TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
CHAN_ENTRY
__field(bool, offchan)
- __field(enum nl80211_channel_type, channel_type)
- __field(bool, channel_type_valid)
__field(unsigned int, wait)
__field(bool, no_cck)
__field(bool, dont_wait_for_ack)
@@ -1650,18 +1667,14 @@ TRACE_EVENT(rdev_mgmt_tx,
WDEV_ASSIGN;
CHAN_ASSIGN(chan);
__entry->offchan = offchan;
- __entry->channel_type = channel_type;
- __entry->channel_type_valid = channel_type_valid;
__entry->wait = wait;
__entry->no_cck = no_cck;
__entry->dont_wait_for_ack = dont_wait_for_ack;
),
- TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", offchan: %s, "
- "channel type: %d, channel type valid: %s, wait: %u, "
- "no cck: %s, dont wait for ack: %s",
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s,"
+ " wait: %u, no cck: %s, dont wait for ack: %s",
WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG,
- BOOL_TO_STR(__entry->offchan), __entry->channel_type,
- BOOL_TO_STR(__entry->channel_type_valid), __entry->wait,
+ BOOL_TO_STR(__entry->offchan), __entry->wait,
BOOL_TO_STR(__entry->no_cck),
BOOL_TO_STR(__entry->dont_wait_for_ack))
);
@@ -1680,7 +1693,7 @@ TRACE_EVENT(rdev_set_noack_map,
NETDEV_ASSIGN;
__entry->noack_map = noack_map;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", noack_map: %u",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", noack_map: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map)
);
@@ -1697,7 +1710,7 @@ TRACE_EVENT(rdev_get_et_sset_count,
NETDEV_ASSIGN;
__entry->sset = sset;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", sset: %d",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
);
@@ -1714,7 +1727,7 @@ TRACE_EVENT(rdev_get_et_strings,
NETDEV_ASSIGN;
__entry->sset = sset;
),
- TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", sset: %u",
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
);
@@ -1723,22 +1736,25 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel,
TP_ARGS(wiphy, wdev)
);
-TRACE_EVENT(rdev_return_channel,
- TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *chan,
- enum nl80211_channel_type type),
- TP_ARGS(wiphy, chan, type),
+TRACE_EVENT(rdev_return_chandef,
+ TP_PROTO(struct wiphy *wiphy, int ret,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, ret, chandef),
TP_STRUCT__entry(
WIPHY_ENTRY
- CHAN_ENTRY
- __field(enum nl80211_channel_type, type)
+ __field(int, ret)
+ CHAN_DEF_ENTRY
),
TP_fast_assign(
WIPHY_ASSIGN;
- CHAN_ASSIGN(chan);
- __entry->type = type;
+ if (ret == 0)
+ CHAN_DEF_ASSIGN(chandef);
+ else
+ CHAN_DEF_ASSIGN((struct cfg80211_chan_def *)NULL);
+ __entry->ret = ret;
),
- TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel type: %d",
- WIPHY_PR_ARG, CHAN_PR_ARG, __entry->type)
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", ret: %d",
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->ret)
);
DEFINE_EVENT(wiphy_wdev_evt, rdev_start_p2p_device,
@@ -1817,7 +1833,7 @@ TRACE_EVENT(cfg80211_send_rx_assoc,
MAC_ASSIGN(bssid, bss->bssid);
CHAN_ASSIGN(bss->channel);
),
- TP_printk(NETDEV_PR_FMT MAC_PR_FMT CHAN_PR_FMT,
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", " CHAN_PR_FMT,
NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
);
@@ -1884,7 +1900,7 @@ TRACE_EVENT(cfg80211_michael_mic_failure,
__entry->key_id = key_id;
memcpy(__entry->tsc, tsc, 6);
),
- TP_printk(NETDEV_PR_FMT MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm",
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm",
NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->key_type,
__entry->key_id, __entry->tsc)
);
@@ -1892,47 +1908,41 @@ TRACE_EVENT(cfg80211_michael_mic_failure,
TRACE_EVENT(cfg80211_ready_on_channel,
TP_PROTO(struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type, unsigned int duration),
- TP_ARGS(wdev, cookie, chan, channel_type, duration),
+ unsigned int duration),
+ TP_ARGS(wdev, cookie, chan, duration),
TP_STRUCT__entry(
WDEV_ENTRY
__field(u64, cookie)
CHAN_ENTRY
- __field(enum nl80211_channel_type, channel_type)
__field(unsigned int, duration)
),
TP_fast_assign(
WDEV_ASSIGN;
__entry->cookie = cookie;
CHAN_ASSIGN(chan);
- __entry->channel_type = channel_type;
__entry->duration = duration;
),
- TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", channel type: %d, duration: %u",
+ TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", duration: %u",
WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG,
- __entry->channel_type, __entry->duration)
+ __entry->duration)
);
TRACE_EVENT(cfg80211_ready_on_channel_expired,
TP_PROTO(struct wireless_dev *wdev, u64 cookie,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type),
- TP_ARGS(wdev, cookie, chan, channel_type),
+ struct ieee80211_channel *chan),
+ TP_ARGS(wdev, cookie, chan),
TP_STRUCT__entry(
WDEV_ENTRY
__field(u64, cookie)
CHAN_ENTRY
- __field(enum nl80211_channel_type, channel_type)
),
TP_fast_assign(
WDEV_ASSIGN;
__entry->cookie = cookie;
CHAN_ASSIGN(chan);
- __entry->channel_type = channel_type;
),
- TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", channel type: %d",
- WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG,
- __entry->channel_type)
+ TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT,
+ WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG)
);
TRACE_EVENT(cfg80211_new_sta,
@@ -1949,7 +1959,7 @@ TRACE_EVENT(cfg80211_new_sta,
MAC_ASSIGN(mac_addr, mac_addr);
SINFO_ASSIGN;
),
- TP_printk(NETDEV_PR_FMT MAC_PR_FMT,
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT,
NETDEV_PR_ARG, MAC_PR_ARG(mac_addr))
);
@@ -2008,40 +2018,35 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify,
NETDEV_PR_ARG, __entry->rssi_event)
);
-TRACE_EVENT(cfg80211_can_beacon_sec_chan,
- TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type),
- TP_ARGS(wiphy, channel, channel_type),
+TRACE_EVENT(cfg80211_reg_can_beacon,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, chandef),
TP_STRUCT__entry(
WIPHY_ENTRY
- CHAN_ENTRY
- __field(enum nl80211_channel_type, channel_type)
+ CHAN_DEF_ENTRY
),
TP_fast_assign(
WIPHY_ASSIGN;
- CHAN_ASSIGN(channel);
- __entry->channel_type = channel_type;
+ CHAN_DEF_ASSIGN(chandef);
),
- TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel_type: %d",
- WIPHY_PR_ARG, CHAN_PR_ARG, __entry->channel_type)
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
);
TRACE_EVENT(cfg80211_ch_switch_notify,
- TP_PROTO(struct net_device *netdev, int freq,
- enum nl80211_channel_type type),
- TP_ARGS(netdev, freq, type),
+ TP_PROTO(struct net_device *netdev,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(netdev, chandef),
TP_STRUCT__entry(
NETDEV_ENTRY
- __field(int, freq)
- __field(enum nl80211_channel_type, type)
+ CHAN_DEF_ENTRY
),
TP_fast_assign(
NETDEV_ASSIGN;
- __entry->freq = freq;
- __entry->type = type;
+ CHAN_DEF_ASSIGN(chandef);
),
- TP_printk(NETDEV_PR_FMT ", freq: %d, type: %d", NETDEV_PR_ARG,
- __entry->freq, __entry->type)
+ TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+ NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
);
DECLARE_EVENT_CLASS(cfg80211_rx_evt,
@@ -2055,7 +2060,7 @@ DECLARE_EVENT_CLASS(cfg80211_rx_evt,
NETDEV_ASSIGN;
MAC_ASSIGN(addr, addr);
),
- TP_printk(NETDEV_PR_FMT MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr))
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr))
);
DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined,
@@ -2089,7 +2094,7 @@ TRACE_EVENT(cfg80211_probe_status,
__entry->cookie = cookie;
__entry->acked = acked;
),
- TP_printk(NETDEV_PR_FMT MAC_PR_FMT ", cookie: %llu, acked: %s",
+ TP_printk(NETDEV_PR_FMT " addr:" MAC_PR_FMT ", cookie: %llu, acked: %s",
NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->cookie,
BOOL_TO_STR(__entry->acked))
);
@@ -2155,6 +2160,29 @@ TRACE_EVENT(cfg80211_report_obss_beacon,
WIPHY_PR_ARG, __entry->freq, __entry->sig_dbm)
);
+TRACE_EVENT(cfg80211_tdls_oper_request,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *peer,
+ enum nl80211_tdls_operation oper, u16 reason_code),
+ TP_ARGS(wiphy, netdev, peer, oper, reason_code),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(enum nl80211_tdls_operation, oper)
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->oper = oper;
+ __entry->reason_code = reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", oper: %d, reason_code %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper,
+ __entry->reason_code)
+ );
+
TRACE_EVENT(cfg80211_scan_done,
TP_PROTO(struct cfg80211_scan_request *request, bool aborted),
TP_ARGS(request, aborted),
@@ -2216,7 +2244,7 @@ TRACE_EVENT(cfg80211_get_bss,
__entry->capa_mask = capa_mask;
__entry->capa_val = capa_val;
),
- TP_printk(WIPHY_PR_FMT CHAN_PR_FMT MAC_PR_FMT ", buf: %#.2x, "
+ TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT ", buf: %#.2x, "
"capa_mask: %d, capa_val: %u", WIPHY_PR_ARG, CHAN_PR_ARG,
MAC_PR_ARG(bssid), ((u8 *)__get_dynamic_array(ssid))[0],
__entry->capa_mask, __entry->capa_val)
@@ -2240,7 +2268,7 @@ TRACE_EVENT(cfg80211_inform_bss_frame,
memcpy(__get_dynamic_array(mgmt), mgmt, len);
__entry->signal = signal;
),
- TP_printk(WIPHY_PR_FMT CHAN_PR_FMT "signal: %d",
+ TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d",
WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal)
);
@@ -2255,7 +2283,7 @@ DECLARE_EVENT_CLASS(cfg80211_bss_evt,
MAC_ASSIGN(bssid, pub->bssid);
CHAN_ASSIGN(pub->channel);
),
- TP_printk(MAC_PR_FMT CHAN_PR_FMT, MAC_PR_ARG(bssid), CHAN_PR_ARG)
+ TP_printk(MAC_PR_FMT ", " CHAN_PR_FMT, MAC_PR_ARG(bssid), CHAN_PR_ARG)
);
DEFINE_EVENT(cfg80211_bss_evt, cfg80211_return_bss,
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 5b6c1df72f31..3cce6e486219 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -944,14 +944,86 @@ static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
return __mcs2bitrate[rate->mcs];
}
+static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
+{
+ static const u32 base[4][10] = {
+ { 6500000,
+ 13000000,
+ 19500000,
+ 26000000,
+ 39000000,
+ 52000000,
+ 58500000,
+ 65000000,
+ 78000000,
+ 0,
+ },
+ { 13500000,
+ 27000000,
+ 40500000,
+ 54000000,
+ 81000000,
+ 108000000,
+ 121500000,
+ 135000000,
+ 162000000,
+ 180000000,
+ },
+ { 29300000,
+ 58500000,
+ 87800000,
+ 117000000,
+ 175500000,
+ 234000000,
+ 263300000,
+ 292500000,
+ 351000000,
+ 390000000,
+ },
+ { 58500000,
+ 117000000,
+ 175500000,
+ 234000000,
+ 351000000,
+ 468000000,
+ 526500000,
+ 585000000,
+ 702000000,
+ 780000000,
+ },
+ };
+ u32 bitrate;
+ int idx;
+
+ if (WARN_ON_ONCE(rate->mcs > 9))
+ return 0;
+
+ idx = rate->flags & (RATE_INFO_FLAGS_160_MHZ_WIDTH |
+ RATE_INFO_FLAGS_80P80_MHZ_WIDTH) ? 3 :
+ rate->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH ? 2 :
+ rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH ? 1 : 0;
+
+ bitrate = base[idx][rate->mcs];
+ bitrate *= rate->nss;
+
+ if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+ bitrate = (bitrate / 9) * 10;
+
+ /* do NOT round down here */
+ return (bitrate + 50000) / 100000;
+}
+
u32 cfg80211_calculate_bitrate(struct rate_info *rate)
{
int modulation, streams, bitrate;
- if (!(rate->flags & RATE_INFO_FLAGS_MCS))
+ if (!(rate->flags & RATE_INFO_FLAGS_MCS) &&
+ !(rate->flags & RATE_INFO_FLAGS_VHT_MCS))
return rate->legacy;
if (rate->flags & RATE_INFO_FLAGS_60G)
return cfg80211_calculate_bitrate_60g(rate);
+ if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
+ return cfg80211_calculate_bitrate_vht(rate);
/* the formula below does only work for MCS values smaller than 32 */
if (WARN_ON_ONCE(rate->mcs >= 32))
@@ -980,6 +1052,106 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
}
EXPORT_SYMBOL(cfg80211_calculate_bitrate);
+int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
+ enum ieee80211_p2p_attr_id attr,
+ u8 *buf, unsigned int bufsize)
+{
+ u8 *out = buf;
+ u16 attr_remaining = 0;
+ bool desired_attr = false;
+ u16 desired_len = 0;
+
+ while (len > 0) {
+ unsigned int iedatalen;
+ unsigned int copy;
+ const u8 *iedata;
+
+ if (len < 2)
+ return -EILSEQ;
+ iedatalen = ies[1];
+ if (iedatalen + 2 > len)
+ return -EILSEQ;
+
+ if (ies[0] != WLAN_EID_VENDOR_SPECIFIC)
+ goto cont;
+
+ if (iedatalen < 4)
+ goto cont;
+
+ iedata = ies + 2;
+
+ /* check WFA OUI, P2P subtype */
+ if (iedata[0] != 0x50 || iedata[1] != 0x6f ||
+ iedata[2] != 0x9a || iedata[3] != 0x09)
+ goto cont;
+
+ iedatalen -= 4;
+ iedata += 4;
+
+ /* check attribute continuation into this IE */
+ copy = min_t(unsigned int, attr_remaining, iedatalen);
+ if (copy && desired_attr) {
+ desired_len += copy;
+ if (out) {
+ memcpy(out, iedata, min(bufsize, copy));
+ out += min(bufsize, copy);
+ bufsize -= min(bufsize, copy);
+ }
+
+
+ if (copy == attr_remaining)
+ return desired_len;
+ }
+
+ attr_remaining -= copy;
+ if (attr_remaining)
+ goto cont;
+
+ iedatalen -= copy;
+ iedata += copy;
+
+ while (iedatalen > 0) {
+ u16 attr_len;
+
+ /* P2P attribute ID & size must fit */
+ if (iedatalen < 3)
+ return -EILSEQ;
+ desired_attr = iedata[0] == attr;
+ attr_len = get_unaligned_le16(iedata + 1);
+ iedatalen -= 3;
+ iedata += 3;
+
+ copy = min_t(unsigned int, attr_len, iedatalen);
+
+ if (desired_attr) {
+ desired_len += copy;
+ if (out) {
+ memcpy(out, iedata, min(bufsize, copy));
+ out += min(bufsize, copy);
+ bufsize -= min(bufsize, copy);
+ }
+
+ if (copy == attr_len)
+ return desired_len;
+ }
+
+ iedata += copy;
+ iedatalen -= copy;
+ attr_remaining = attr_len - copy;
+ }
+
+ cont:
+ len -= ies[1] + 2;
+ ies += ies[1] + 2;
+ }
+
+ if (attr_remaining && desired_attr)
+ return -EILSEQ;
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(cfg80211_get_p2p_attr);
+
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
u32 beacon_int)
{
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 6488d2dbc1d7..f9680c9cf9b3 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -784,6 +784,9 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct cfg80211_chan_def chandef = {
+ .width = NL80211_CHAN_WIDTH_20_NOHT,
+ };
int freq, err;
switch (wdev->iftype) {
@@ -797,8 +800,12 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
return freq;
if (freq == 0)
return -EINVAL;
+ chandef.center_freq1 = freq;
+ chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (!chandef.chan)
+ return -EINVAL;
mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT);
+ err = cfg80211_set_monitor_channel(rdev, &chandef);
mutex_unlock(&rdev->devlist_mtx);
return err;
case NL80211_IFTYPE_MESH_POINT:
@@ -807,9 +814,12 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
return freq;
if (freq == 0)
return -EINVAL;
+ chandef.center_freq1 = freq;
+ chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (!chandef.chan)
+ return -EINVAL;
mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_set_mesh_freq(rdev, wdev, freq,
- NL80211_CHAN_NO_HT);
+ err = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
mutex_unlock(&rdev->devlist_mtx);
return err;
default:
@@ -823,8 +833,8 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
- struct ieee80211_channel *chan;
- enum nl80211_channel_type channel_type;
+ struct cfg80211_chan_def chandef;
+ int ret;
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
@@ -835,10 +845,10 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
if (!rdev->ops->get_channel)
return -EINVAL;
- chan = rdev_get_channel(rdev, wdev, &channel_type);
- if (!chan)
- return -EINVAL;
- freq->m = chan->center_freq;
+ ret = rdev_get_channel(rdev, wdev, &chandef);
+ if (ret)
+ return ret;
+ freq->m = chandef.chan->center_freq;
freq->e = 6;
return 0;
default:
@@ -895,7 +905,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
return 0;
}
- return rdev_set_tx_power(rdev, type, DBM_TO_MBM(dbm));
+ return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));
}
static int cfg80211_wext_giwtxpower(struct net_device *dev,
@@ -914,7 +924,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
if (!rdev->ops->get_tx_power)
return -EOPNOTSUPP;
- err = rdev_get_tx_power(rdev, &val);
+ err = rdev_get_tx_power(rdev, wdev, &val);
if (err)
return err;
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index 1f773f668d1a..873af63187c0 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -119,7 +119,16 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
* channel we disconnected above and reconnect below.
*/
if (chan && !wdev->wext.connect.ssid_len) {
- err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT);
+ struct cfg80211_chan_def chandef = {
+ .width = NL80211_CHAN_WIDTH_20_NOHT,
+ .center_freq1 = freq,
+ };
+
+ chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (chandef.chan)
+ err = cfg80211_set_monitor_channel(rdev, &chandef);
+ else
+ err = -EINVAL;
goto out;
}