From 2b6a6a90c31c550f839e905484f5253b9d24e33d Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 5 Dec 2013 06:53:41 +0100 Subject: [PATCH 001/121] ath10k: fix WEP Shared authentication Frames received from FW were treated incorrectly. This led to stations being unable to associate to WEP Shared ath10k AP. This was indicated by a bizarre hostapd message: Unsupported authentication algorithm (36088) Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 1260a8d15dc3..9ea333a7789c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -937,7 +937,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); - if (fc & IEEE80211_FCTL_PROTECTED) { + /* FW delivers WEP Shared Auth frame with Protected Bit set and + * encrypted payload. However in case of PMF it delivers decrypted + * frames with Protected Bit set. */ + if (ieee80211_has_protected(hdr->frame_control) && + !ieee80211_is_auth(hdr->frame_control)) { status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; hdr->frame_control = __cpu_to_le16(fc & From 2256940010aab059dc238ab81b7b562b17942ec8 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 13 Dec 2013 13:44:16 +0100 Subject: [PATCH 002/121] ath10k: handle TKIP MIC error correctly We should check MIC error flag base on rx_attention, to have consistent status of MIC failure and FCS error. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt.h | 1 + drivers/net/wireless/ath/ath10k/htt_rx.c | 15 +++++++++++++++ drivers/net/wireless/ath/ath10k/txrx.c | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 7fc7919ea5f5..b93ae355bc08 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1183,6 +1183,7 @@ struct htt_rx_info { } rate; bool fcs_err; bool amsdu_more; + bool mic_err; }; struct ath10k_htt { diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index fcb534f2f28f..fe8bd1b59f0e 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -838,6 +838,20 @@ static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb) return false; } +static bool ath10k_htt_rx_has_mic_err(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + u32 flags; + + rxd = (void *)skb->data - sizeof(*rxd); + flags = __le32_to_cpu(rxd->attention.flags); + + if (flags & RX_ATTENTION_FLAGS_TKIP_MIC_ERR) + return true; + + return false; +} + static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) { struct htt_rx_desc *rxd; @@ -960,6 +974,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, info.skb = msdu_head; info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head); + info.mic_err = ath10k_htt_rx_has_mic_err(msdu_head); info.signal = ATH10K_DEFAULT_NOISE_FLOOR; info.signal += rx->ppdu.combined_rssi; diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 22829803f087..74f45fa6f428 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -231,7 +231,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) ~IEEE80211_FCTL_PROTECTED); } - if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) + if (info->mic_err) status->flag |= RX_FLAG_MMIC_ERROR; if (info->fcs_err) From 453cdb61c015f80e492dda217110353cd09eef18 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 13 Dec 2013 10:44:43 +0100 Subject: [PATCH 003/121] ath10k: fix band reporting for mgmt CCK frames on 5GHz Although CCK modulation isn't expected for 11a if it happened it made ath10k report wrong band (2GHz) for a mgmt frame that were actually received on 5Ghz band. Frequency would also decoded incorrectly too. In case of 5GHz-only devices this triggered mac80211 WARN_ON because there was no according sband pointer to be found. The patch should fix delivery of such frames by using different means to acquire band parameter. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 9ea333a7789c..3ec6c9a84e84 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -875,6 +875,7 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) struct wmi_mgmt_rx_event_v2 *ev_v2; struct wmi_mgmt_rx_hdr_v1 *ev_hdr; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_channel *ch; struct ieee80211_hdr *hdr; u32 rx_status; u32 channel; @@ -927,7 +928,25 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) if (rx_status & WMI_RX_STATUS_ERR_MIC) status->flag |= RX_FLAG_MMIC_ERROR; - status->band = phy_mode_to_band(phy_mode); + /* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to + * MODE_11B. This means phy_mode is not a reliable source for the band + * of mgmt rx. */ + + ch = ar->scan_channel; + if (!ch) + ch = ar->rx_channel; + + if (ch) { + status->band = ch->band; + + if (phy_mode == MODE_11B && + status->band == IEEE80211_BAND_5GHZ) + ath10k_dbg(ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n"); + } else { + ath10k_warn("using (unreliable) phy_mode to extract band for mgmt rx\n"); + status->band = phy_mode_to_band(phy_mode); + } + status->freq = ieee80211_channel_to_frequency(channel, status->band); status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; status->rate_idx = get_rate_idx(rate, status->band); From f259509baafcf180c6146bc5bcc0ae7688e5017a Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 10 Dec 2013 16:20:39 +0100 Subject: [PATCH 004/121] ath10k: split the if_limits and if_comb Split the interface limits and inteface combination, to reflect the 10.X capabilites (no P2P, no STA and 8 VAP). kvalo: reverse order of ATH10K_FW_FEATURE_WMI_10X test, fix checkpath warnings Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ce9ef3499ecb..52f618740cd9 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3464,14 +3464,12 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { }, }; -#ifdef CONFIG_ATH10K_DFS_CERTIFIED -static const struct ieee80211_iface_limit ath10k_if_dfs_limits[] = { +static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, }; -#endif static const struct ieee80211_iface_combination ath10k_if_comb[] = { { @@ -3481,19 +3479,22 @@ static const struct ieee80211_iface_combination ath10k_if_comb[] = { .num_different_channels = 1, .beacon_int_infra_match = true, }, -#ifdef CONFIG_ATH10K_DFS_CERTIFIED +}; + +static const struct ieee80211_iface_combination ath10k_10x_if_comb[] = { { - .limits = ath10k_if_dfs_limits, - .n_limits = ARRAY_SIZE(ath10k_if_dfs_limits), + .limits = ath10k_10x_if_limits, + .n_limits = ARRAY_SIZE(ath10k_10x_if_limits), .max_interfaces = 8, .num_different_channels = 1, .beacon_int_infra_match = true, +#ifdef CONFIG_ATH10K_DFS_CERTIFIED .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | BIT(NL80211_CHAN_WIDTH_20) | BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80), - } #endif + }, }; static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) @@ -3717,8 +3718,15 @@ int ath10k_mac_register(struct ath10k *ar) */ ar->hw->queues = 4; - ar->hw->wiphy->iface_combinations = ath10k_if_comb; - ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_10x_if_comb); + } else { + ar->hw->wiphy->iface_combinations = ath10k_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_if_comb); + } ar->hw->netdev_features = NETIF_F_HW_CSUM; From d354181f4d9a4b91ddbc90327cba6692ef3779a1 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 10 Dec 2013 16:20:40 +0100 Subject: [PATCH 005/121] ath10k: introduce NO_P2P fw feature flag Not each ath10k FW track supports P2P (10.X for instance does not) This new firmware feature flag allows to turn off P2P interface type. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 3 +++ drivers/net/wireless/ath/ath10k/mac.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 79726e0fe2f0..035cbf6d1abe 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -306,6 +306,9 @@ enum ath10k_fw_features { /* firmware support tx frame management over WMI, otherwise it's HTT */ ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2, + /* Firmware does not support P2P */ + ATH10K_FW_FEATURE_NO_P2P = 3, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 52f618740cd9..5b45f3a8ec40 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3673,9 +3673,12 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO); + BIT(NL80211_IFTYPE_AP); + + if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features)) + ar->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); ar->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | From 194ff52d42fd0b55fe0fcfbf4586ae9d7ab1f780 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 30 Dec 2013 23:12:37 +0100 Subject: [PATCH 006/121] cfg80211/mac80211: correct qos-map locking Since the RTNL can't always be held, use wdev/sdata locking for the qos-map dereference in mac80211. This requires cfg80211 to consistently lock it, which it was missing in one place. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 +- net/wireless/util.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 09d2e58a2ba7..fd1020e791d8 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3854,7 +3854,7 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy, new_qos_map = NULL; } - old_qos_map = rtnl_dereference(sdata->qos_map); + old_qos_map = sdata_dereference(sdata->qos_map, sdata); rcu_assign_pointer(sdata->qos_map, new_qos_map); if (old_qos_map) kfree_rcu(old_qos_map, rcu_head); diff --git a/net/wireless/util.c b/net/wireless/util.c index 5618888853b2..329b0efb3ded 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -879,7 +879,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, dev->ieee80211_ptr->use_4addr = false; dev->ieee80211_ptr->mesh_id_up_len = 0; + wdev_lock(dev->ieee80211_ptr); rdev_set_qos_map(rdev, dev, NULL); + wdev_unlock(dev->ieee80211_ptr); switch (otype) { case NL80211_IFTYPE_AP: From fe0219cc505578636a6ed38d493e41ea5d599e3e Mon Sep 17 00:00:00 2001 From: Salil Kapur Date: Thu, 31 Oct 2013 23:17:46 +0530 Subject: [PATCH 007/121] NFC: mei_phy: Using kfree_skb() instead of kfree() Using kfree_skb() instead of kfree() for struct sk_buff Signed-off-by: Salil Kapur Signed-off-by: Samuel Ortiz --- drivers/nfc/mei_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 1d7860551985..11c7cbdade66 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c @@ -127,7 +127,7 @@ void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context) reply_size = mei_cl_recv(device, skb->data, MEI_NFC_MAX_READ); if (reply_size < MEI_NFC_HEADER_SIZE) { - kfree(skb); + kfree_skb(skb); return; } From 34f7e57b231ec4fd943d0d172fb7c1bb143f1da8 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Thu, 14 Nov 2013 17:07:32 -0800 Subject: [PATCH 008/121] NFC: pn544: Refactor hw_config values Some of the EEPROM configurations that are assigned by the PN544 driver are set by the firmware and should not be modified by the driver. Others are certain user mode configurations that are currently getting set to values that shouldn't necessarily be dictated by the driver. This patch changes most user and system mode configurations to the firmware defaults. Signed-off-by: Arman Uguray Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 51e21a87cd84..3df4a109cfad 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -195,42 +195,42 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev) {{0x9e, 0xaa}, 0x01}, - {{0x9b, 0xd1}, 0x0d}, - {{0x9b, 0xd2}, 0x24}, - {{0x9b, 0xd3}, 0x0a}, - {{0x9b, 0xd4}, 0x22}, - {{0x9b, 0xd5}, 0x08}, - {{0x9b, 0xd6}, 0x1e}, - {{0x9b, 0xdd}, 0x1c}, + {{0x9b, 0xd1}, 0x17}, + {{0x9b, 0xd2}, 0x58}, + {{0x9b, 0xd3}, 0x10}, + {{0x9b, 0xd4}, 0x47}, + {{0x9b, 0xd5}, 0x0c}, + {{0x9b, 0xd6}, 0x37}, + {{0x9b, 0xdd}, 0x33}, - {{0x9b, 0x84}, 0x13}, - {{0x99, 0x81}, 0x7f}, - {{0x99, 0x31}, 0x70}, + {{0x9b, 0x84}, 0x00}, + {{0x99, 0x81}, 0x79}, + {{0x99, 0x31}, 0x79}, {{0x98, 0x00}, 0x3f}, - {{0x9f, 0x09}, 0x00}, + {{0x9f, 0x09}, 0x02}, {{0x9f, 0x0a}, 0x05}, {{0x9e, 0xd1}, 0xa1}, - {{0x99, 0x23}, 0x00}, - - {{0x9e, 0x74}, 0x80}, + {{0x99, 0x23}, 0x01}, + {{0x9e, 0x74}, 0x00}, + {{0x9e, 0x90}, 0x00}, {{0x9f, 0x28}, 0x10}, - {{0x9f, 0x35}, 0x14}, + {{0x9f, 0x35}, 0x04}, - {{0x9f, 0x36}, 0x60}, + {{0x9f, 0x36}, 0x11}, {{0x9c, 0x31}, 0x00}, - {{0x9c, 0x32}, 0xc8}, + {{0x9c, 0x32}, 0x00}, - {{0x9c, 0x19}, 0x40}, + {{0x9c, 0x19}, 0x0a}, - {{0x9c, 0x1a}, 0x40}, + {{0x9c, 0x1a}, 0x0a}, {{0x9c, 0x0c}, 0x00}, @@ -240,13 +240,13 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev) {{0x9c, 0x13}, 0x00}, - {{0x98, 0xa2}, 0x0e}, + {{0x98, 0xa2}, 0x09}, - {{0x98, 0x93}, 0x40}, + {{0x98, 0x93}, 0x00}, - {{0x98, 0x7d}, 0x02}, + {{0x98, 0x7d}, 0x08}, {{0x98, 0x7e}, 0x00}, - {{0x9f, 0xc8}, 0x01}, + {{0x9f, 0xc8}, 0x00}, }; struct hw_config *p = hw_config; int count = ARRAY_SIZE(hw_config); From 249eb5bd74553da189fbff90c20c6d884a1db71e Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 13 Nov 2013 01:00:07 +0100 Subject: [PATCH 009/121] NFC: Return driver failure upon unknown event reception If the device is polling, this will trigger a netlink event to notify userspace about the polling error. Signed-off-by: Samuel Ortiz --- net/nfc/hci/core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 3b9610031baa..d45b638e77c7 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -335,11 +335,8 @@ exit: kfree_skb(skb); exit_noskb: - if (r) { - /* TODO: There was an error dispatching the event, - * how to propagate up to nfc core? - */ - } + if (r) + nfc_hci_driver_failure(hdev, r); } static void nfc_hci_cmd_timeout(unsigned long data) From 43d53c29dd8548404256c05573ff557c927d214b Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Sat, 30 Nov 2013 16:14:57 +0100 Subject: [PATCH 010/121] NFC: llcp: Fix possible memory leak while sending I frames If sending was not completed due to low memory condition msg_data was not free before returning from function. Signed-off-by: Szymon Janc Signed-off-by: Samuel Ortiz --- net/nfc/llcp_commands.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 693cd1aad582..80057a818737 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -684,8 +684,10 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, pdu = llcp_allocate_pdu(sock, LLCP_PDU_I, frag_len + LLCP_SEQUENCE_SIZE); - if (pdu == NULL) + if (pdu == NULL) { + kfree(msg_data); return -ENOMEM; + } skb_put(pdu, LLCP_SEQUENCE_SIZE); From 11bfb1c4b94fe24a83ebeae5c3310280f9606e0e Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Sat, 30 Nov 2013 16:59:23 +0100 Subject: [PATCH 011/121] NFC: llcp: Use default MIU if none was specified on connect If MIUX is not present in CONNECT or CC use default MIU value (128) instead of one announced durring link setup. This was affecting Bluetooth handover with Android 4.3+ NCI stack. Signed-off-by: Szymon Janc Signed-off-by: Samuel Ortiz --- net/nfc/llcp_commands.c | 2 +- net/nfc/llcp_core.c | 1 - net/nfc/llcp_sock.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 80057a818737..bec6ed15f503 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -675,7 +675,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, do { remote_miu = sock->remote_miu > LLCP_MAX_MIU ? - local->remote_miu : sock->remote_miu; + LLCP_DEFAULT_MIU : sock->remote_miu; frag_len = min_t(size_t, remote_miu, remaining_len); diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index 1349074e1ffc..6184bd1fba3a 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -943,7 +943,6 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local, new_sock->local = nfc_llcp_local_get(local); new_sock->rw = sock->rw; new_sock->miux = sock->miux; - new_sock->remote_miu = local->remote_miu; new_sock->nfc_protocol = sock->nfc_protocol; new_sock->dsap = ssap; new_sock->target_idx = local->target_idx; diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index 69fbc8dadba7..4a53bb58a463 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -700,7 +700,6 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr, llcp_sock->dev = dev; llcp_sock->local = nfc_llcp_local_get(local); - llcp_sock->remote_miu = llcp_sock->local->remote_miu; llcp_sock->ssap = nfc_llcp_get_local_ssap(local); if (llcp_sock->ssap == LLCP_SAP_MAX) { ret = -ENOMEM; From a434c2407467a76c0e1416c45f7b31cfbe1b6b3b Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sun, 22 Dec 2013 01:00:20 +0100 Subject: [PATCH 012/121] NFC: Only warn on SE discovery error SE discovery errors are currently overwriting the dev_up() return error. This is wrong for many reasons: - We don't want to report an error if we actually brought the device up but it failed to discover SEs. By doing so we pretend we don't have an NFC functional device even we do. The only thing we could not do was checking for SEs availability. This is the false negative case. - In some cases the actual device power up failed but the SE discovery succeeded. Userspace then believes the device is up while it's not. This is the false positive case. Signed-off-by: Samuel Ortiz --- net/nfc/core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/nfc/core.c b/net/nfc/core.c index 02ab34132157..c1903f439aac 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -133,11 +133,8 @@ int nfc_dev_up(struct nfc_dev *dev) dev->dev_up = true; /* We have to enable the device before discovering SEs */ - if (dev->ops->discover_se) { - rc = dev->ops->discover_se(dev); - if (rc) - pr_warn("SE discovery failed\n"); - } + if (dev->ops->discover_se && dev->ops->discover_se(dev)) + pr_err("SE discovery failed\n"); error: device_unlock(&dev->dev); From ea87a5efa9efa84cd48fbf7a969d951b32c9e5e4 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sat, 4 Jan 2014 03:23:35 +0100 Subject: [PATCH 013/121] NFC: pn533: Frame is invalid if ccid.datalen is 0 Some ACR122 firmwares seem to send 0 length data frames. Before using that length as a data index, we check that it's not 0. If it is we report the frame as being invalid. Reported-by: Arthur Taylor Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 3df19e657bc1..cf1a87bb74f8 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -521,6 +521,9 @@ static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev) if (frame->ccid.type != 0x83) return false; + if (!frame->ccid.datalen) + return false; + if (frame->data[frame->ccid.datalen - 2] == 0x63) return false; From 444fb98eed98f7292a83f9bf123d1c78f171327e Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:12 +0100 Subject: [PATCH 014/121] NFC: digital: Add a note about asynchronous functions This explains how and why the timeout parameter must be handled by the driver implementation. Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 36acecd5f06c..81af21e9bcd4 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -122,6 +122,16 @@ typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev, * switch_rf to turn the radio on. A call to in|tg_configure_hw must turn * the device radio on. * @abort_cmd: Discard the last sent command. + * + * Notes: Asynchronous functions have a timeout parameter. It is the driver + * responsibility to call the digital stack back through the + * nfc_digital_cmd_complete_t callback when no RF respsonse has been + * received within the specified time (in milliseconds). In that case the + * driver must set the resp sk_buff to ERR_PTR(-ETIMEDOUT). + * Since the digital stack serializes commands to be sent, it's mandatory + * for the driver to handle the timeout correctly. Otherwise the stack + * would not be able to send new commands, waiting for the reply of the + * current one. */ struct nfc_digital_ops { int (*in_configure_hw)(struct nfc_digital_dev *ddev, int type, From 67af1d7a0fb4b5397cb093c0fe98d5b95516c228 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:13 +0100 Subject: [PATCH 015/121] NFC: digital: Fix incorrect use of ERR_PTR and PTR_ERR macros It's bad to use these macros when not dealing with error code. this patch changes calls to these macros with correct casts. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 07bbc24fb4c7..e42ecb66e712 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -472,7 +472,7 @@ int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb) static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { - u8 rf_tech = PTR_ERR(arg); + u8 rf_tech = (unsigned long)arg; if (IS_ERR(resp)) return; @@ -508,7 +508,7 @@ static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did, ddev->skb_add_crc(skb); rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete, - ERR_PTR(rf_tech)); + (void *)(unsigned long)rf_tech); if (rc) kfree_skb(skb); From 4f319e3251937a73719b13397d738640622ba513 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:14 +0100 Subject: [PATCH 016/121] NFC: digital: Use NFC_NFCID3_MAXSIZE from nfc.h This removes the declaration of NFCID3 size in digital_dep.c and now uses the one from nfc.h. This also removes a faulty and unneeded call to max(). Reported-by: Dan Carpenter Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index e42ecb66e712..470a0b4016ed 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -32,7 +32,6 @@ #define DIGITAL_ATR_REQ_MIN_SIZE 16 #define DIGITAL_ATR_REQ_MAX_SIZE 64 -#define DIGITAL_NFCID3_LEN ((u8)8) #define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30 #define DIGITAL_GB_BIT 0x02 @@ -206,10 +205,9 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev, atr_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; atr_req->cmd = DIGITAL_CMD_ATR_REQ; if (target->nfcid2_len) - memcpy(atr_req->nfcid3, target->nfcid2, - max(target->nfcid2_len, DIGITAL_NFCID3_LEN)); + memcpy(atr_req->nfcid3, target->nfcid2, NFC_NFCID2_MAXSIZE); else - get_random_bytes(atr_req->nfcid3, DIGITAL_NFCID3_LEN); + get_random_bytes(atr_req->nfcid3, NFC_NFCID3_MAXSIZE); atr_req->did = 0; atr_req->bs = 0; From c36aeba8c072d20a08e8546483494ab0f6458963 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 5 Jan 2014 00:08:05 +0400 Subject: [PATCH 017/121] NFC: port100: Fix device leak port100_probe() calls usb_get_dev(), but there is no usb_put_dev() in port100_disconnect(). The patch adds one. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 8a0571eb2627..a8555f81cbba 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -1509,6 +1509,7 @@ static void port100_disconnect(struct usb_interface *interface) usb_free_urb(dev->in_urb); usb_free_urb(dev->out_urb); + usb_put_dev(dev->udev); kfree(dev->cmd); From fa9be5f009e8cfc695a2cd71617fc620ab5c1c18 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 23 Dec 2013 14:15:13 -0800 Subject: [PATCH 018/121] NFC: NCI: Cancel cmd_timer in nci_close_device() nci_close_device() sends nci reset command to the device. If there is no response for this command, nci request timeout occurs first and then cmd timeout happens. Because command timer has started after sending the command. We are immediately flushing command workqueue after nci timeout. Later we will try to schedule cmd_work in command timer which leads to a crash. Cancel cmd_timer before flushing the workqueue to fix the problem. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f0e955e3a385..0a2ee83c3bd3 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -361,6 +361,8 @@ static int nci_close_device(struct nci_dev *ndev) msecs_to_jiffies(NCI_RESET_TIMEOUT)); clear_bit(NCI_INIT, &ndev->flags); + del_timer_sync(&ndev->cmd_timer); + /* Flush cmd wq */ flush_workqueue(ndev->cmd_wq); From 1b000789a4fe5f2013cc120a97d8c9b9c57b5431 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 19 Dec 2013 10:47:48 +0100 Subject: [PATCH 019/121] mac80211: add tracing for ieee80211_sta_set_buffered This is useful for debugging issues with drivers using this function (erroneously), so add tracing for the API call. Change-Id: Ice9d7eabb8fecbac188f0a741920d3488de700ec Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 2 ++ net/mac80211/trace.h | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4576ba0ff221..8da3af2c6b61 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1485,6 +1485,8 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, if (WARN_ON(tid >= IEEE80211_NUM_TIDS)) return; + trace_api_sta_set_buffered(sta->local, pubsta, tid, buffered); + if (buffered) set_bit(tid, &sta->driver_buffered_tids); else diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index da9366632f37..a0b0aea76525 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1835,6 +1835,33 @@ TRACE_EVENT(api_eosp, ) ); +TRACE_EVENT(api_sta_set_buffered, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, + u8 tid, bool buffered), + + TP_ARGS(local, sta, tid, buffered), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(u8, tid) + __field(bool, buffered) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->tid = tid; + __entry->buffered = buffered; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT " tid:%d buffered:%d", + LOCAL_PR_ARG, STA_PR_ARG, __entry->tid, __entry->buffered + ) +); + /* * Tracing for internal functions * (which may also be called in response to driver calls) From e03ad6eade141daf0df07f1c312e8ae702327939 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jan 2014 17:22:30 +0100 Subject: [PATCH 020/121] nl80211: move vendor/testmode event skb functions out of ifdef The vendor/testmode event skb functions are needed outside the ifdef for vendor-specific events, so move them out. Reported-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 98 +++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b4f40fe84a01..20857126f742 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6795,6 +6795,55 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, return NULL; } +struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, + enum nl80211_commands cmd, + enum nl80211_attrs attr, + int vendor_event_idx, + int approxlen, gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + const struct nl80211_vendor_cmd_info *info; + + switch (cmd) { + case NL80211_CMD_TESTMODE: + if (WARN_ON(vendor_event_idx != -1)) + return NULL; + info = NULL; + break; + case NL80211_CMD_VENDOR: + if (WARN_ON(vendor_event_idx < 0 || + vendor_event_idx >= wiphy->n_vendor_events)) + return NULL; + info = &wiphy->vendor_events[vendor_event_idx]; + break; + default: + WARN_ON(1); + return NULL; + } + + return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, + cmd, attr, info, gfp); +} +EXPORT_SYMBOL(__cfg80211_alloc_event_skb); + +void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + + if (data->nla_type == NL80211_ATTR_VENDOR_DATA) + mcgrp = NL80211_MCGRP_VENDOR; + + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, + mcgrp, gfp); +} +EXPORT_SYMBOL(__cfg80211_send_event_skb); + #ifdef CONFIG_NL80211_TESTMODE static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) { @@ -6921,55 +6970,6 @@ static int nl80211_testmode_dump(struct sk_buff *skb, rtnl_unlock(); return err; } - -struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, - enum nl80211_commands cmd, - enum nl80211_attrs attr, - int vendor_event_idx, - int approxlen, gfp_t gfp) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - const struct nl80211_vendor_cmd_info *info; - - switch (cmd) { - case NL80211_CMD_TESTMODE: - if (WARN_ON(vendor_event_idx != -1)) - return NULL; - info = NULL; - break; - case NL80211_CMD_VENDOR: - if (WARN_ON(vendor_event_idx < 0 || - vendor_event_idx >= wiphy->n_vendor_events)) - return NULL; - info = &wiphy->vendor_events[vendor_event_idx]; - break; - default: - WARN_ON(1); - return NULL; - } - - return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, - cmd, attr, info, gfp); -} -EXPORT_SYMBOL(__cfg80211_alloc_event_skb); - -void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) -{ - struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; - void *hdr = ((void **)skb->cb)[1]; - struct nlattr *data = ((void **)skb->cb)[2]; - enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; - - nla_nest_end(skb, data); - genlmsg_end(skb, hdr); - - if (data->nla_type == NL80211_ATTR_VENDOR_DATA) - mcgrp = NL80211_MCGRP_VENDOR; - - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, - mcgrp, gfp); -} -EXPORT_SYMBOL(__cfg80211_send_event_skb); #endif static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) From 4cd3c4ecfcbf07fe58ce7de4de61cf85ddc10e78 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 15:47:15 +0100 Subject: [PATCH 021/121] mac80211: clean up netdev debugfs macros a bit Clean up the file macros a bit and use that to remove the unnecessary format function for the tkip MIC test file that really is write-only. Signed-off-by: Johannes Berg --- net/mac80211/debugfs_netdev.c | 60 +++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 04b5a14c8a05..b62cbe82954c 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -133,7 +133,15 @@ static ssize_t ieee80211_if_fmt_##name( \ jiffies_to_msecs(sdata->field)); \ } -#define __IEEE80211_IF_FILE(name, _write) \ +#define _IEEE80211_IF_FILE_OPS(name, _read, _write) \ +static const struct file_operations name##_ops = { \ + .read = (_read), \ + .write = (_write), \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +} + +#define _IEEE80211_IF_FILE_R_FN(name) \ static ssize_t ieee80211_if_read_##name(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ @@ -141,28 +149,34 @@ static ssize_t ieee80211_if_read_##name(struct file *file, \ return ieee80211_if_read(file->private_data, \ userbuf, count, ppos, \ ieee80211_if_fmt_##name); \ -} \ -static const struct file_operations name##_ops = { \ - .read = ieee80211_if_read_##name, \ - .write = (_write), \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ } -#define __IEEE80211_IF_FILE_W(name) \ +#define _IEEE80211_IF_FILE_W_FN(name) \ static ssize_t ieee80211_if_write_##name(struct file *file, \ const char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ return ieee80211_if_write(file->private_data, userbuf, count, \ ppos, ieee80211_if_parse_##name); \ -} \ -__IEEE80211_IF_FILE(name, ieee80211_if_write_##name) +} +#define IEEE80211_IF_FILE_R(name) \ + _IEEE80211_IF_FILE_R_FN(name) \ + _IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name, NULL) + +#define IEEE80211_IF_FILE_W(name) \ + _IEEE80211_IF_FILE_W_FN(name) \ + _IEEE80211_IF_FILE_OPS(name, NULL, ieee80211_if_write_##name) + +#define IEEE80211_IF_FILE_RW(name) \ + _IEEE80211_IF_FILE_R_FN(name) \ + _IEEE80211_IF_FILE_W_FN(name) \ + _IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name, \ + ieee80211_if_write_##name) #define IEEE80211_IF_FILE(name, field, format) \ - IEEE80211_IF_FMT_##format(name, field) \ - __IEEE80211_IF_FILE(name, NULL) + IEEE80211_IF_FMT_##format(name, field) \ + IEEE80211_IF_FILE_R(name) /* common attributes */ IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); @@ -199,7 +213,7 @@ ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata, return len; } -__IEEE80211_IF_FILE(hw_queues, NULL); +IEEE80211_IF_FILE_R(hw_queues); /* STA attributes */ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); @@ -275,14 +289,7 @@ static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, return -EINVAL; } - -__IEEE80211_IF_FILE_W(smps); - -static ssize_t ieee80211_if_fmt_tkip_mic_test( - const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) -{ - return -EOPNOTSUPP; -} +IEEE80211_IF_FILE_RW(smps); static ssize_t ieee80211_if_parse_tkip_mic_test( struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) @@ -349,8 +356,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( return buflen; } - -__IEEE80211_IF_FILE_W(tkip_mic_test); +IEEE80211_IF_FILE_W(tkip_mic_test); static ssize_t ieee80211_if_fmt_uapsd_queues( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) @@ -378,7 +384,7 @@ static ssize_t ieee80211_if_parse_uapsd_queues( return buflen; } -__IEEE80211_IF_FILE_W(uapsd_queues); +IEEE80211_IF_FILE_RW(uapsd_queues); static ssize_t ieee80211_if_fmt_uapsd_max_sp_len( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) @@ -406,7 +412,7 @@ static ssize_t ieee80211_if_parse_uapsd_max_sp_len( return buflen; } -__IEEE80211_IF_FILE_W(uapsd_max_sp_len); +IEEE80211_IF_FILE_RW(uapsd_max_sp_len); /* AP attributes */ IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC); @@ -419,7 +425,7 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast( return scnprintf(buf, buflen, "%u\n", skb_queue_len(&sdata->u.ap.ps.bc_buf)); } -__IEEE80211_IF_FILE(num_buffered_multicast, NULL); +IEEE80211_IF_FILE_R(num_buffered_multicast); /* IBSS attributes */ static ssize_t ieee80211_if_fmt_tsf( @@ -470,7 +476,7 @@ static ssize_t ieee80211_if_parse_tsf( return buflen; } -__IEEE80211_IF_FILE_W(tsf); +IEEE80211_IF_FILE_RW(tsf); /* WDS attributes */ From ef04a29737dd08352fdf6431d119ca636d664efe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 15:56:59 +0100 Subject: [PATCH 022/121] mac80211: handle station TX latency allocation errors When the station's TX latency data structures need to be allocated, handle failures properly and also free all the structures if there are any other problems. Move the allocation code up so that allocation failures don't trigger rate control algorithm calls. Reported-by: ZHAO Gang Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 70 +++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8da3af2c6b61..703081d04e88 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -300,6 +300,35 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, if (!sta) return NULL; + rcu_read_lock(); + tx_latency = rcu_dereference(local->tx_latency); + /* init stations Tx latency statistics && TID bins */ + if (tx_latency) { + sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS * + sizeof(struct ieee80211_tx_latency_stat), + GFP_ATOMIC); + if (!sta->tx_lat) { + rcu_read_unlock(); + goto free; + } + + if (tx_latency->n_ranges) { + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { + /* size of bins is size of the ranges +1 */ + sta->tx_lat[i].bin_count = + tx_latency->n_ranges + 1; + sta->tx_lat[i].bins = + kcalloc(sta->tx_lat[i].bin_count, + sizeof(u32), GFP_ATOMIC); + if (!sta->tx_lat[i].bins) { + rcu_read_unlock(); + goto free; + } + } + } + } + rcu_read_unlock(); + spin_lock_init(&sta->lock); INIT_WORK(&sta->drv_unblock_wk, sta_unblock); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); @@ -324,10 +353,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) ewma_init(&sta->chain_signal_avg[i], 1024, 8); - if (sta_prepare_rate_control(local, sta, gfp)) { - kfree(sta); - return NULL; - } + if (sta_prepare_rate_control(local, sta, gfp)) + goto free; for (i = 0; i < IEEE80211_NUM_TIDS; i++) { /* @@ -371,34 +398,17 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, } } - rcu_read_lock(); - - tx_latency = rcu_dereference(local->tx_latency); - /* init stations Tx latency statistics && TID bins */ - if (tx_latency) - sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS * - sizeof(struct ieee80211_tx_latency_stat), - GFP_ATOMIC); - - /* - * if Tx latency and bins are enabled and the previous allocation - * succeeded - */ - if (tx_latency && tx_latency->n_ranges && sta->tx_lat) - for (i = 0; i < IEEE80211_NUM_TIDS; i++) { - /* size of bins is size of the ranges +1 */ - sta->tx_lat[i].bin_count = - tx_latency->n_ranges + 1; - sta->tx_lat[i].bins = kcalloc(sta->tx_lat[i].bin_count, - sizeof(u32), - GFP_ATOMIC); - } - - rcu_read_unlock(); - sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); - return sta; + +free: + if (sta->tx_lat) { + for (i = 0; i < IEEE80211_NUM_TIDS; i++) + kfree(sta->tx_lat[i].bins); + kfree(sta->tx_lat); + } + kfree(sta); + return NULL; } static int sta_info_insert_check(struct sta_info *sta) From 5b0ec94f9cfdcd10f6eb4df75fba9404c72fea4d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 16:02:34 +0100 Subject: [PATCH 023/121] mac80211: fix memory leak in register_hw() error path Move the internal scan request allocation below the last sanity check in ieee80211_register_hw() to avoid leaking memory if the sanity check actually triggers. Reported-by: ZHAO Gang Signed-off-by: Johannes Berg --- net/mac80211/main.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2bd5b552b2f6..d767cfb9b45f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -846,17 +846,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) /* TODO: consider VHT for RX chains, hopefully it's the same */ } - local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + - sizeof(void *) * channels, GFP_KERNEL); - if (!local->int_scan_req) - return -ENOMEM; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!local->hw.wiphy->bands[band]) - continue; - local->int_scan_req->rates[band] = (u32) -1; - } - /* if low-level driver supports AP, we also support VLAN */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); @@ -880,6 +869,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) return -EINVAL; } + local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + + sizeof(void *) * channels, GFP_KERNEL); + if (!local->int_scan_req) + return -ENOMEM; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!local->hw.wiphy->bands[band]) + continue; + local->int_scan_req->rates[band] = (u32) -1; + } + #ifndef CONFIG_MAC80211_MESH /* mesh depends on Kconfig, but drivers should set it if they want */ local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT); From 6b5895d93d460105a384775bc675221ef377d40a Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Fri, 20 Dec 2013 16:39:52 +0800 Subject: [PATCH 024/121] mac80211: enable WME for peer mesh STA Enable the WME for peer mesh STA so that the driver, such as wcn36xx, will pick this up and enabling it in HW. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index cf83217103f9..e8f60aa2e848 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -437,6 +437,7 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); set_sta_flag(sta, WLAN_STA_WME); + sta->sta.wme = true; return sta; } From 60a4fe0ae97b515f174f9bcb157cbaeff0dab8ac Mon Sep 17 00:00:00 2001 From: Ujjal Roy Date: Mon, 6 Jan 2014 21:56:11 +0530 Subject: [PATCH 025/121] cfg80211: fix wext-compat for getting retry value While getting the retry limit, wext-compat returns the value without updating the flag for retry->flags is 0. Also in this case, it updates long retry flag when short and long retry value are unequal. So, iwconfig never showing "Retry short limit" and showing "Retry long limit" when both values are unequal. Updated the flags and corrected the condition properly. Signed-off-by: Ujjal Roy Signed-off-by: Johannes Berg --- net/wireless/wext-compat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index e7c6e862580d..051d961d4e28 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -412,9 +412,9 @@ int cfg80211_wext_giwretry(struct net_device *dev, * First return short value, iwconfig will ask long value * later if needed */ - retry->flags |= IW_RETRY_LIMIT; + retry->flags |= IW_RETRY_LIMIT | IW_RETRY_SHORT; retry->value = wdev->wiphy->retry_short; - if (wdev->wiphy->retry_long != wdev->wiphy->retry_short) + if (wdev->wiphy->retry_long == wdev->wiphy->retry_short) retry->flags |= IW_RETRY_LONG; return 0; From 057d5f4ba1e421185a8e7e0b7fadf253d41a3e83 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 19 Dec 2013 10:25:15 -0800 Subject: [PATCH 026/121] mac80211: sync dtim_count to TSF On starting a mesh or AP BSS, the interface dtim_count countdown should match that of the driver TSF. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 1 + net/mac80211/debugfs_netdev.c | 1 + net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mesh.c | 1 + net/mac80211/util.c | 41 +++++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fd1020e791d8..f9ae9b85d4c1 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1035,6 +1035,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, return err; } + ieee80211_recalc_dtim(local, sdata); ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(dev); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index b62cbe82954c..ebf80f3abd83 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -474,6 +474,7 @@ static ssize_t ieee80211_if_parse_tsf( } } + ieee80211_recalc_dtim(local, sdata); return buflen; } IEEE80211_IF_FILE_RW(tsf); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 953b9e294547..3701930c6649 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1800,6 +1800,8 @@ ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, int ieee80211_cs_headroom(struct ieee80211_local *local, struct cfg80211_crypto_settings *crypto, enum nl80211_iftype iftype); +void ieee80211_recalc_dtim(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 5a74b249ba35..5b919cab1de0 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -807,6 +807,7 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) return -ENOMEM; } + ieee80211_recalc_dtim(local, sdata); ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(sdata->dev); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index df00f1978a77..676dc0967f37 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2734,3 +2734,44 @@ int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, return ret; } EXPORT_SYMBOL(ieee80211_parse_p2p_noa); + +void ieee80211_recalc_dtim(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + u64 tsf = drv_get_tsf(local, sdata); + u64 dtim_count = 0; + u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024; + u8 dtim_period = sdata->vif.bss_conf.dtim_period; + struct ps_data *ps; + u8 bcns_from_dtim; + + if (tsf == -1ULL || !beacon_int || !dtim_period) + return; + + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + if (!sdata->bss) + return; + + ps = &sdata->bss->ps; + } else if (ieee80211_vif_is_mesh(&sdata->vif)) { + ps = &sdata->u.mesh.ps; + } else { + return; + } + + /* + * actually finds last dtim_count, mac80211 will update in + * __beacon_add_tim(). + * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period + */ + do_div(tsf, beacon_int); + bcns_from_dtim = do_div(tsf, dtim_period); + /* just had a DTIM */ + if (!bcns_from_dtim) + dtim_count = 0; + else + dtim_count = dtim_period - bcns_from_dtim; + + ps->dtim_count = dtim_count; +} From 79012f8bed417847ec028bd67547eae4eb832205 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 21:53:27 +0100 Subject: [PATCH 027/121] mac80211_hwsim: fix a print message The prefix should be mac80211_hwsim, not mac_80211_hwsim. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 9c0cc8ded021..9e7d7df03a47 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2618,7 +2618,7 @@ static int __init init_mac80211_hwsim(void) return 0; failed_nl: - printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n"); + printk(KERN_DEBUG "mac80211_hwsim: failed initializing netlink\n"); return err; failed_mon: From 1907299867431fd899ae630a29b08b878ca1e50f Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:15 -0800 Subject: [PATCH 028/121] NFC: NCI: Don't reverse local general bytes Local general bytes returned by nfc_get_local_general_bytes() are already in correct order. We don't need to reverse them. Remove local_gb[] local array as it's not needed any more. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 0a2ee83c3bd3..6fbb7bab46e7 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -414,8 +414,6 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); struct nci_set_config_param param; - __u8 local_gb[NFC_MAX_GT_LEN]; - int i; param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); if ((param.val == NULL) || (param.len == 0)) @@ -424,11 +422,7 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) if (param.len > NFC_MAX_GT_LEN) return -EINVAL; - for (i = 0; i < param.len; i++) - local_gb[param.len-1-i] = param.val[i]; - param.id = NCI_PN_ATR_REQ_GEN_BYTES; - param.val = local_gb; return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); From 86e8586ed5beea15ce7c359f02a1084c2da93bc7 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:17 -0800 Subject: [PATCH 029/121] NFC: NCI: Add setup handler Some drivers require special configuration while initializing. This patch adds setup handler for this custom configuration. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 6126f1f992b4..5c6fadd63483 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -68,6 +68,7 @@ struct nci_ops { int (*open)(struct nci_dev *ndev); int (*close)(struct nci_dev *ndev); int (*send)(struct nci_dev *ndev, struct sk_buff *skb); + int (*setup)(struct nci_dev *ndev); }; #define NCI_MAX_SUPPORTED_RF_INTERFACES 4 diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 6fbb7bab46e7..29c1caf3e975 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -301,6 +301,9 @@ static int nci_open_device(struct nci_dev *ndev) rc = __nci_request(ndev, nci_reset_req, 0, msecs_to_jiffies(NCI_RESET_TIMEOUT)); + if (ndev->ops->setup(ndev)) + ndev->ops->setup(ndev); + if (!rc) { rc = __nci_request(ndev, nci_init_req, 0, msecs_to_jiffies(NCI_INIT_TIMEOUT)); From 22c15bf30b70ab2eae300f093ffc64e182620aba Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:18 -0800 Subject: [PATCH 030/121] NFC: NCI: Add set_config API This API can be used by drivers to send their custom configuration using SET_CONFIG NCI command to the device. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 5c6fadd63483..2b93b77b210c 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -155,6 +155,7 @@ void nci_free_device(struct nci_dev *ndev); int nci_register_device(struct nci_dev *ndev); void nci_unregister_device(struct nci_dev *ndev); int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); +int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val); static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev, unsigned int len, diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 29c1caf3e975..46bda010bf11 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -413,6 +413,22 @@ static int nci_dev_down(struct nfc_dev *nfc_dev) return nci_close_device(ndev); } +int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) +{ + struct nci_set_config_param param; + + if (!val || !len) + return 0; + + param.id = id; + param.len = len; + param.val = val; + + return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); +} +EXPORT_SYMBOL(nci_set_config); + static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); From f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:19 -0800 Subject: [PATCH 031/121] NFC: nfcmrvl: Initial commit for Marvell NFC driver This patch adds NFC support for Marvell 8897 NFC-over-USB chipset. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 1 + drivers/nfc/Makefile | 1 + drivers/nfc/nfcmrvl/Kconfig | 23 ++ drivers/nfc/nfcmrvl/Makefile | 9 + drivers/nfc/nfcmrvl/main.c | 145 +++++++++++ drivers/nfc/nfcmrvl/nfcmrvl.h | 42 ++++ drivers/nfc/nfcmrvl/usb.c | 459 ++++++++++++++++++++++++++++++++++ 7 files changed, 680 insertions(+) create mode 100644 drivers/nfc/nfcmrvl/Kconfig create mode 100644 drivers/nfc/nfcmrvl/Makefile create mode 100644 drivers/nfc/nfcmrvl/main.c create mode 100644 drivers/nfc/nfcmrvl/nfcmrvl.h create mode 100644 drivers/nfc/nfcmrvl/usb.c diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index c1fb20603338..fe20e1cc0545 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -58,5 +58,6 @@ config NFC_PORT100 source "drivers/nfc/pn544/Kconfig" source "drivers/nfc/microread/Kconfig" +source "drivers/nfc/nfcmrvl/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index c715fe8582a8..56ab822ba03d 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_NFC_WILINK) += nfcwilink.o obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o obj-$(CONFIG_NFC_SIM) += nfcsim.o obj-$(CONFIG_NFC_PORT100) += port100.o +obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig new file mode 100644 index 000000000000..5e18afd9abe2 --- /dev/null +++ b/drivers/nfc/nfcmrvl/Kconfig @@ -0,0 +1,23 @@ +config NFC_MRVL + tristate "Marvell NFC driver support" + depends on NFC_NCI + help + The core driver to support Marvell NFC devices. + + This driver is required if you want to support + Marvell NFC device 8897. + + Say Y here to compile Marvell NFC driver into the kernel or + say M to compile it as module. + +config NFC_MRVL_USB + tristate "Marvell NFC-over-USB driver" + depends on NFC_MRVL && USB + help + Marvell NFC-over-USB driver. + + This driver provides support for Marvell NFC-over-USB devices: + 8897. + + Say Y here to compile support for Marvell NFC-over-USB driver + into the kernel or say M to compile it as module. diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile new file mode 100644 index 000000000000..97a0de72dc01 --- /dev/null +++ b/drivers/nfc/nfcmrvl/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for NFCMRVL NCI based NFC driver +# + +nfcmrvl-y += main.o +obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o + +nfcmrvl_usb-y += usb.o +obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c new file mode 100644 index 000000000000..396fc270ffc3 --- /dev/null +++ b/drivers/nfc/nfcmrvl/main.c @@ -0,0 +1,145 @@ +/* + * Marvell NFC driver: major functions + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include +#include +#include +#include +#include "nfcmrvl.h" + +#define VERSION "1.0" + +static int nfcmrvl_nci_open(struct nci_dev *ndev) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + int err; + + if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return 0; + + err = priv->if_ops->nci_open(priv); + + if (err) + clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags); + + return err; +} + +static int nfcmrvl_nci_close(struct nci_dev *ndev) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + + if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return 0; + + priv->if_ops->nci_close(priv); + + return 0; +} + +static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + + nfc_info(priv->dev, "send entry, len %d\n", skb->len); + + skb->dev = (void *)ndev; + + if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return -EBUSY; + + return priv->if_ops->nci_send(priv, skb); +} + +static struct nci_ops nfcmrvl_nci_ops = { + .open = nfcmrvl_nci_open, + .close = nfcmrvl_nci_close, + .send = nfcmrvl_nci_send, +}; + +struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, + struct nfcmrvl_if_ops *ops, + struct device *dev) +{ + struct nfcmrvl_private *priv; + int rc; + u32 protocols; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return ERR_PTR(-ENOMEM); + + priv->drv_data = drv_data; + priv->if_ops = ops; + priv->dev = dev; + + protocols = NFC_PROTO_JEWEL_MASK + | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK + | NFC_PROTO_ISO14443_MASK + | NFC_PROTO_ISO14443_B_MASK + | NFC_PROTO_NFC_DEP_MASK; + + priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0); + if (!priv->ndev) { + nfc_err(dev, "nci_allocate_device failed"); + return ERR_PTR(-ENOMEM); + } + + nci_set_drvdata(priv->ndev, priv); + + rc = nci_register_device(priv->ndev); + if (rc) { + nfc_err(dev, "nci_register_device failed %d", rc); + nci_free_device(priv->ndev); + return ERR_PTR(rc); + } + + nfc_info(dev, "registered with nci successfully\n"); + return priv; +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev); + +void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) +{ + struct nci_dev *ndev = priv->ndev; + + nci_unregister_device(ndev); + nci_free_device(ndev); + kfree(priv); +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev); + +int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count) +{ + struct sk_buff *skb; + + skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, count), data, count); + nci_recv_frame(priv->ndev, skb); + + return count; +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell NFC driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h new file mode 100644 index 000000000000..a007175f77fe --- /dev/null +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -0,0 +1,42 @@ +/** + * Marvell NFC driver + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + **/ + +/* Define private flags: */ +#define NFCMRVL_NCI_RUNNING 1 + +#define NFCMRVL_NCI_MAX_EVENT_SIZE 260 + +struct nfcmrvl_private { + struct nci_dev *ndev; + unsigned long flags; + void *drv_data; + struct device *dev; + struct nfcmrvl_if_ops *if_ops; +}; + +struct nfcmrvl_if_ops { + int (*nci_open) (struct nfcmrvl_private *priv); + int (*nci_close) (struct nfcmrvl_private *priv); + int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb); +}; + +void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv); +int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count); +struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, + struct nfcmrvl_if_ops *ops, + struct device *dev); diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c new file mode 100644 index 000000000000..3221ca37d6c9 --- /dev/null +++ b/drivers/nfc/nfcmrvl/usb.c @@ -0,0 +1,459 @@ +/** + * Marvell NFC-over-USB driver: USB interface related functions + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + **/ + +#include +#include +#include +#include +#include +#include "nfcmrvl.h" + +#define VERSION "1.0" + +static struct usb_device_id nfcmrvl_table[] = { + { USB_DEVICE_INTERFACE_CLASS(0x1286, 0x2046, 0xff) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, nfcmrvl_table); + +#define NFCMRVL_USB_BULK_RUNNING 1 +#define NFCMRVL_USB_SUSPENDING 2 + +struct nfcmrvl_usb_drv_data { + struct usb_device *udev; + struct usb_interface *intf; + unsigned long flags; + struct work_struct waker; + struct usb_anchor tx_anchor; + struct usb_anchor bulk_anchor; + struct usb_anchor deferred; + int tx_in_flight; + /* protects tx_in_flight */ + spinlock_t txlock; + struct usb_endpoint_descriptor *bulk_tx_ep; + struct usb_endpoint_descriptor *bulk_rx_ep; + int suspend_count; + struct nfcmrvl_private *priv; +}; + +static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) +{ + unsigned long flags; + int rv; + + spin_lock_irqsave(&drv_data->txlock, flags); + rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + if (!rv) + drv_data->tx_in_flight++; + spin_unlock_irqrestore(&drv_data->txlock, flags); + + return rv; +} + +static void nfcmrvl_bulk_complete(struct urb *urb) +{ + struct nfcmrvl_usb_drv_data *drv_data = urb->context; + int err; + + dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d", + urb, urb->status, urb->actual_length); + + if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) + return; + + if (!urb->status) { + if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer, + urb->actual_length) < 0) + nfc_err(&drv_data->udev->dev, "corrupted Rx packet"); + } + + if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) + return; + + usb_anchor_urb(urb, &drv_data->bulk_anchor); + usb_mark_last_busy(drv_data->udev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + /* -EPERM: urb is being killed; + * -ENODEV: device got disconnected + */ + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p failed to resubmit (%d)", urb, -err); + usb_unanchor_urb(urb); + } +} + +static int +nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) +{ + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE; + + if (!drv_data->bulk_rx_ep) + return -ENODEV; + + urb = usb_alloc_urb(0, mem_flags); + if (!urb) + return -ENOMEM; + + buf = kmalloc(size, mem_flags); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(drv_data->udev, + drv_data->bulk_rx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, + nfcmrvl_bulk_complete, drv_data); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_mark_last_busy(drv_data->udev); + usb_anchor_urb(urb, &drv_data->bulk_anchor); + + err = usb_submit_urb(urb, mem_flags); + if (err) { + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p submission failed (%d)", urb, -err); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static void nfcmrvl_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct nci_dev *ndev = (struct nci_dev *)skb->dev; + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + + nfc_info(priv->dev, "urb %p status %d count %d", + urb, urb->status, urb->actual_length); + + spin_lock(&drv_data->txlock); + drv_data->tx_in_flight--; + spin_unlock(&drv_data->txlock); + + kfree(urb->setup_packet); + kfree_skb(skb); +} + +static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + int err; + + err = usb_autopm_get_interface(drv_data->intf); + if (err) + return err; + + drv_data->intf->needs_remote_wakeup = 1; + + err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); + if (err) + goto failed; + + set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); + + usb_autopm_put_interface(drv_data->intf); + return 0; + +failed: + usb_autopm_put_interface(drv_data->intf); + return err; +} + +static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) +{ + usb_kill_anchored_urbs(&drv_data->bulk_anchor); +} + +static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + int err; + + cancel_work_sync(&drv_data->waker); + + clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + + nfcmrvl_usb_stop_traffic(drv_data); + usb_kill_anchored_urbs(&drv_data->tx_anchor); + err = usb_autopm_get_interface(drv_data->intf); + if (err) + goto failed; + + drv_data->intf->needs_remote_wakeup = 0; + usb_autopm_put_interface(drv_data->intf); + +failed: + usb_scuttle_anchored_urbs(&drv_data->deferred); + return 0; +} + +static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, + struct sk_buff *skb) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + struct urb *urb; + unsigned int pipe; + int err; + + if (!drv_data->bulk_tx_ep) + return -ENODEV; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + pipe = usb_sndbulkpipe(drv_data->udev, + drv_data->bulk_tx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len, + nfcmrvl_tx_complete, skb); + + err = nfcmrvl_inc_tx(drv_data); + if (err) { + usb_anchor_urb(urb, &drv_data->deferred); + schedule_work(&drv_data->waker); + err = 0; + goto done; + } + + usb_anchor_urb(urb, &drv_data->tx_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p submission failed (%d)", urb, -err); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } else { + usb_mark_last_busy(drv_data->udev); + } + +done: + usb_free_urb(urb); + return err; +} + +static struct nfcmrvl_if_ops usb_ops = { + .nci_open = nfcmrvl_usb_nci_open, + .nci_close = nfcmrvl_usb_nci_close, + .nci_send = nfcmrvl_usb_nci_send, +}; + +static void nfcmrvl_waker(struct work_struct *work) +{ + struct nfcmrvl_usb_drv_data *drv_data = + container_of(work, struct nfcmrvl_usb_drv_data, waker); + int err; + + err = usb_autopm_get_interface(drv_data->intf); + if (err) + return; + + usb_autopm_put_interface(drv_data->intf); +} + +static int nfcmrvl_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *ep_desc; + struct nfcmrvl_usb_drv_data *drv_data; + struct nfcmrvl_private *priv; + int i; + struct usb_device *udev = interface_to_usbdev(intf); + + nfc_info(&udev->dev, "intf %p id %p", intf, id); + + drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + ep_desc = &intf->cur_altsetting->endpoint[i].desc; + + if (!drv_data->bulk_tx_ep && + usb_endpoint_is_bulk_out(ep_desc)) { + drv_data->bulk_tx_ep = ep_desc; + continue; + } + + if (!drv_data->bulk_rx_ep && + usb_endpoint_is_bulk_in(ep_desc)) { + drv_data->bulk_rx_ep = ep_desc; + continue; + } + } + + if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) + return -ENODEV; + + drv_data->udev = udev; + drv_data->intf = intf; + + INIT_WORK(&drv_data->waker, nfcmrvl_waker); + spin_lock_init(&drv_data->txlock); + + init_usb_anchor(&drv_data->tx_anchor); + init_usb_anchor(&drv_data->bulk_anchor); + init_usb_anchor(&drv_data->deferred); + + priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops, + &drv_data->udev->dev); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + drv_data->priv = priv; + priv->dev = &drv_data->udev->dev; + + usb_set_intfdata(intf, drv_data); + + return 0; +} + +static void nfcmrvl_disconnect(struct usb_interface *intf) +{ + struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); + + if (!drv_data) + return; + + nfc_info(&drv_data->udev->dev, "intf %p", intf); + + nfcmrvl_nci_unregister_dev(drv_data->priv); + + usb_set_intfdata(drv_data->intf, NULL); +} + +#ifdef CONFIG_PM +static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); + + nfc_info(&drv_data->udev->dev, "intf %p", intf); + + if (drv_data->suspend_count++) + return 0; + + spin_lock_irq(&drv_data->txlock); + if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { + set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + } else { + spin_unlock_irq(&drv_data->txlock); + drv_data->suspend_count--; + return -EBUSY; + } + + nfcmrvl_usb_stop_traffic(drv_data); + usb_kill_anchored_urbs(&drv_data->tx_anchor); + + return 0; +} + +static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) +{ + struct urb *urb; + int err; + + while ((urb = usb_get_from_anchor(&drv_data->deferred))) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) + break; + + drv_data->tx_in_flight++; + } + usb_scuttle_anchored_urbs(&drv_data->deferred); +} + +static int nfcmrvl_resume(struct usb_interface *intf) +{ + struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); + int err = 0; + + nfc_info(&drv_data->udev->dev, "intf %p", intf); + + if (--drv_data->suspend_count) + return 0; + + if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) + goto done; + + if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { + err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); + if (err) { + clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + goto failed; + } + + nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); + } + + spin_lock_irq(&drv_data->txlock); + nfcmrvl_play_deferred(drv_data); + clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + + return 0; + +failed: + usb_scuttle_anchored_urbs(&drv_data->deferred); +done: + spin_lock_irq(&drv_data->txlock); + clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + + return err; +} +#endif + +static struct usb_driver nfcmrvl_usb_driver = { + .name = "nfcmrvl", + .probe = nfcmrvl_probe, + .disconnect = nfcmrvl_disconnect, +#ifdef CONFIG_PM + .suspend = nfcmrvl_suspend, + .resume = nfcmrvl_resume, + .reset_resume = nfcmrvl_resume, +#endif + .id_table = nfcmrvl_table, + .supports_autosuspend = 1, + .disable_hub_initiated_lpm = 1, + .soft_unbind = 1, +}; +module_usb_driver(nfcmrvl_usb_driver); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell NFC-over-USB driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL v2"); From 15203b4c79aaeb443f89ac3190eed3447bf2178b Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:20 -0800 Subject: [PATCH 032/121] NFC: nfcmrvl: Add setup handler Marvell nfc device provides support for external coexistance control. It allows Device Host to inhibit the NFCC from polling when required by asserting a GPIO pin. A second pin allows the DH to have feedback on the current NFCC state. The required configuration for this feature is done in setup handler. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 15 +++++++++++++++ drivers/nfc/nfcmrvl/nfcmrvl.h | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 396fc270ffc3..5f91d4571429 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -66,10 +66,25 @@ static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) return priv->if_ops->nci_send(priv, skb); } +static int nfcmrvl_nci_setup(struct nci_dev *ndev) +{ + __u8 val; + + val = NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED; + nci_set_config(ndev, NFCMRVL_NOT_ALLOWED_ID, 1, &val); + val = NFCMRVL_GPIO_PIN_NFC_ACTIVE; + nci_set_config(ndev, NFCMRVL_ACTIVE_ID, 1, &val); + val = NFCMRVL_EXT_COEX_ENABLE; + nci_set_config(ndev, NFCMRVL_EXT_COEX_ID, 1, &val); + + return 0; +} + static struct nci_ops nfcmrvl_nci_ops = { .open = nfcmrvl_nci_open, .close = nfcmrvl_nci_close, .send = nfcmrvl_nci_send, + .setup = nfcmrvl_nci_setup, }; struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index a007175f77fe..54c4a956bd45 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -19,6 +19,12 @@ /* Define private flags: */ #define NFCMRVL_NCI_RUNNING 1 +#define NFCMRVL_EXT_COEX_ID 0xE0 +#define NFCMRVL_NOT_ALLOWED_ID 0xE1 +#define NFCMRVL_ACTIVE_ID 0xE2 +#define NFCMRVL_EXT_COEX_ENABLE 1 +#define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA +#define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB #define NFCMRVL_NCI_MAX_EVENT_SIZE 260 struct nfcmrvl_private { From 87ee475ef62504609368a767f6ad2e903a442078 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 7 Jan 2014 13:09:38 +0200 Subject: [PATCH 033/121] mac80211: clean up garbage in comment Not clear how this landed here. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/wpa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 7313d379c0d3..21448d629b15 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -127,7 +127,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) * APs with pairwise keys should never receive Michael MIC * errors for non-zero keyidx because these are reserved for * group keys and only the AP is sending real multicast - * frames in the BSS. ( + * frames in the BSS. */ return RX_DROP_UNUSABLE; } From 40791942ec6befaad04073ed97adb8ab04ab43f3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 7 Jan 2014 13:10:20 +0200 Subject: [PATCH 034/121] mac80211: simplify code in ieee80211_prepare_and_rx_handle No need to assign the return value of prepare_for_handlers to a variable if the only usage is to test it. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5a2afe9583a8..8cb20d7ff637 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3191,13 +3191,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (void *)skb->data; - int prepares; rx->skb = skb; status->rx_flags |= IEEE80211_RX_RA_MATCH; - prepares = prepare_for_handlers(rx, hdr); - if (!prepares) + if (!prepare_for_handlers(rx, hdr)) return false; if (!consume) { From 3c2723f503e8decc420b377a3215089a7980cf08 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jan 2014 16:23:24 +0100 Subject: [PATCH 035/121] mac80211: clean up prepare_for_handlers() return value Using an int with 0/1 is not very common, make the function return a bool instead with the same values (false/true). Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 8cb20d7ff637..c24ca0d0f469 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3076,8 +3076,8 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) /* main receive path */ -static int prepare_for_handlers(struct ieee80211_rx_data *rx, - struct ieee80211_hdr *hdr) +static bool prepare_for_handlers(struct ieee80211_rx_data *rx, + struct ieee80211_hdr *hdr) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct sk_buff *skb = rx->skb; @@ -3088,29 +3088,29 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!bssid && !sdata->u.mgd.use_4addr) - return 0; + return false; if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { if (!(sdata->dev->flags & IFF_PROMISC) || sdata->u.mgd.use_4addr) - return 0; + return false; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } break; case NL80211_IFTYPE_ADHOC: if (!bssid) - return 0; + return false; if (ether_addr_equal(sdata->vif.addr, hdr->addr2) || ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2)) - return 0; + return false; if (ieee80211_is_beacon(hdr->frame_control)) { - return 1; + return true; } else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) { - return 0; + return false; } else if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { if (!(sdata->dev->flags & IFF_PROMISC)) - return 0; + return false; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } else if (!rx->sta) { int rate_idx; @@ -3126,7 +3126,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { if (!(sdata->dev->flags & IFF_PROMISC)) - return 0; + return false; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } @@ -3135,7 +3135,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, case NL80211_IFTYPE_AP: if (!bssid) { if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) - return 0; + return false; } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) { /* * Accept public action frames even when the @@ -3145,26 +3145,26 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, */ if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) - return 0; + return false; if (ieee80211_is_public_action(hdr, skb->len)) - return 1; + return true; if (!ieee80211_is_beacon(hdr->frame_control)) - return 0; + return false; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } break; case NL80211_IFTYPE_WDS: if (bssid || !ieee80211_is_data(hdr->frame_control)) - return 0; + return false; if (!ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2)) - return 0; + return false; break; case NL80211_IFTYPE_P2P_DEVICE: if (!ieee80211_is_public_action(hdr, skb->len) && !ieee80211_is_probe_req(hdr->frame_control) && !ieee80211_is_probe_resp(hdr->frame_control) && !ieee80211_is_beacon(hdr->frame_control)) - return 0; + return false; if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) && !multicast) status->rx_flags &= ~IEEE80211_RX_RA_MATCH; @@ -3175,7 +3175,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, break; } - return 1; + return true; } /* From 349b196044d38a3bf5be2b4dd2fe28ca2f12a258 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 7 Jan 2014 13:11:41 +0200 Subject: [PATCH 036/121] mac80211: allow to set smps mode to OFF in AP mode In managed mode, we should not ask for OFF mode because the power settings may still require DYNAMIC. In AP mode, this should be allowed since the default settings is OFF and AUTOMATIC is not allowed. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 9a8be8f69224..fab7b91923e0 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -479,10 +479,9 @@ void ieee80211_request_smps(struct ieee80211_vif *vif, vif->type != NL80211_IFTYPE_AP)) return; - if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - if (vif->type == NL80211_IFTYPE_STATION) { + if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) + smps_mode = IEEE80211_SMPS_AUTOMATIC; if (sdata->u.mgd.driver_smps_mode == smps_mode) return; sdata->u.mgd.driver_smps_mode = smps_mode; From 48e1044515967a0d88ee076045b2141535557d8e Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 6 Jan 2014 23:34:37 +0100 Subject: [PATCH 037/121] NFC: digital: Set current target active on activate_target() call The curr_protocol field of nfc_digital_dev structure used to determine if a target is currently active was set too soon, immediately when a target is found. This is not good since there is no other way than deactivate_target() to reset curr_protocol and if activate_target() is not called, the target remains active and it's not possible to put the device in poll mode anymore. With this patch curr_protocol is set when nfc core activates a target, puts a device up, or when an ATR_REQ is received in target mode. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_core.c | 28 ++++++++++++++++++++++++++-- net/nfc/digital_dep.c | 2 ++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 09fc95439955..c129d1571ca6 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -339,7 +339,6 @@ int digital_target_found(struct nfc_digital_dev *ddev, pr_debug("rf_tech=%d, protocol=%d\n", rf_tech, protocol); ddev->curr_rf_tech = rf_tech; - ddev->curr_protocol = protocol; if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) { ddev->skb_add_crc = digital_skb_add_crc_none; @@ -541,8 +540,14 @@ static int digital_dep_link_up(struct nfc_dev *nfc_dev, __u8 comm_mode, __u8 *gb, size_t gb_len) { struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + int rc; - return digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len); + rc = digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len); + + if (!rc) + ddev->curr_protocol = NFC_PROTO_NFC_DEP; + + return rc; } static int digital_dep_link_down(struct nfc_dev *nfc_dev) @@ -557,6 +562,20 @@ static int digital_dep_link_down(struct nfc_dev *nfc_dev) static int digital_activate_target(struct nfc_dev *nfc_dev, struct nfc_target *target, __u32 protocol) { + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + + if (ddev->poll_tech_count) { + pr_err("Can't activate a target while polling\n"); + return -EBUSY; + } + + if (ddev->curr_protocol) { + pr_err("A target is already active\n"); + return -EBUSY; + } + + ddev->curr_protocol = protocol; + return 0; } @@ -565,6 +584,11 @@ static void digital_deactivate_target(struct nfc_dev *nfc_dev, { struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + if (!ddev->curr_protocol) { + pr_err("No active target\n"); + return; + } + ddev->curr_protocol = 0; } diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 470a0b4016ed..562bec9ad8d9 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -681,6 +681,8 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, ddev->skb_check_crc = digital_skb_check_crc_none; } + ddev->curr_protocol = NFC_PROTO_NFC_DEP_MASK; + rc = ddev->skb_check_crc(resp); if (rc) { PROTOCOL_ERR("14.4.1.6"); From b711ad524bf5a6a078c4d0a1a44ca1db204802f6 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 6 Jan 2014 23:34:48 +0100 Subject: [PATCH 038/121] NFC: digital: Set rf tech and crc functions when receiving a PSL_REQ This patch sets the correct rf tech value and crc functions in target mode when receiving a PSL_REQ, as done when receiving an ATR_REQ. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 44 ++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 562bec9ad8d9..43e450f78d0a 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -380,6 +380,33 @@ int digital_in_send_dep_req(struct nfc_digital_dev *ddev, data_exch); } +static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + ddev->curr_rf_tech = rf_tech; + + ddev->skb_add_crc = digital_skb_add_crc_none; + ddev->skb_check_crc = digital_skb_check_crc_none; + + if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) + return; + + switch (ddev->curr_rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + ddev->skb_add_crc = digital_skb_add_crc_a; + ddev->skb_check_crc = digital_skb_check_crc_a; + break; + + case NFC_DIGITAL_RF_TECH_212F: + case NFC_DIGITAL_RF_TECH_424F: + ddev->skb_add_crc = digital_skb_add_crc_f; + ddev->skb_check_crc = digital_skb_check_crc_f; + break; + + default: + break; + } +} + static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { @@ -475,6 +502,8 @@ static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev, if (IS_ERR(resp)) return; + digital_tg_set_rf_tech(ddev, rf_tech); + digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); digital_tg_listen(ddev, 1500, digital_tg_recv_dep_req, NULL); @@ -659,16 +688,10 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) { min_size = DIGITAL_ATR_REQ_MIN_SIZE + 2; - - ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_106A; - ddev->skb_add_crc = digital_skb_add_crc_a; - ddev->skb_check_crc = digital_skb_check_crc_a; + digital_tg_set_rf_tech(ddev, NFC_DIGITAL_RF_TECH_106A); } else { min_size = DIGITAL_ATR_REQ_MIN_SIZE + 1; - - ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_212F; - ddev->skb_add_crc = digital_skb_add_crc_f; - ddev->skb_check_crc = digital_skb_check_crc_f; + digital_tg_set_rf_tech(ddev, NFC_DIGITAL_RF_TECH_212F); } if (resp->len < min_size) { @@ -676,11 +699,6 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, goto exit; } - if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) { - ddev->skb_add_crc = digital_skb_add_crc_none; - ddev->skb_check_crc = digital_skb_check_crc_none; - } - ddev->curr_protocol = NFC_PROTO_NFC_DEP_MASK; rc = ddev->skb_check_crc(resp); From 685328b296acc810541d2532957912690273c64a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:24:00 +0100 Subject: [PATCH 039/121] mac80211: remove channel_change_time This value is no longer used by mac80211, and practically no driver ever set it to a correct value anyway, so remove it. Signed-off-by: Johannes Berg --- drivers/net/wireless/adm8211.c | 1 - drivers/net/wireless/at76c50x-usb.c | 1 - drivers/net/wireless/ath/ath10k/mac.c | 1 - drivers/net/wireless/ath/ath5k/base.c | 1 - drivers/net/wireless/ath/ath9k/htc_drv_init.c | 1 - drivers/net/wireless/ath/ath9k/init.c | 1 - drivers/net/wireless/ath/carl9170/main.c | 12 ------------ .../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 1 - drivers/net/wireless/cw1200/main.c | 1 - drivers/net/wireless/mac80211_hwsim.c | 1 - drivers/net/wireless/mwl8k.c | 2 -- drivers/net/wireless/p54/main.c | 1 - drivers/net/wireless/rtlwifi/base.c | 1 - drivers/net/wireless/ti/wl1251/main.c | 1 - drivers/net/wireless/ti/wlcore/main.c | 1 - drivers/staging/winbond/wbusb.c | 1 - include/net/mac80211.h | 3 --- 17 files changed, 31 deletions(-) diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index cfce83e1f273..54afde0463dd 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1866,7 +1866,6 @@ static int adm8211_probe(struct pci_dev *pdev, dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - dev->channel_change_time = 1000; dev->max_signal = 100; /* FIXME: find better value */ dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */ diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 34c8a33cac06..8cfd1d6841b1 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -2112,7 +2112,6 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) priv->pm_period = 0; /* unit us */ - priv->hw->channel_change_time = 100000; return priv; } diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ce9ef3499ecb..747c8bd74980 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3704,7 +3704,6 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->vif_data_size = sizeof(struct ath10k_vif); - ar->hw->channel_change_time = 5000; ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL; ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 69f58b073e85..fe6878b4ecd0 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2549,7 +2549,6 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) hw->wiphy->available_antennas_rx = 0x3; hw->extra_tx_headroom = 2; - hw->channel_change_time = 5000; /* * Mark the device as detached to avoid processing diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index c3676bf1d6c4..bcdb50af56d3 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -748,7 +748,6 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; hw->queues = 4; - hw->channel_change_time = 5000; hw->max_listen_interval = 1; hw->vif_data_size = sizeof(struct ath9k_htc_vif); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 609b7e13f3f0..d8787692c9d4 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -950,7 +950,6 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->queues = 4; hw->max_rates = 4; - hw->channel_change_time = 5000; hw->max_listen_interval = 1; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 349fa22a921a..5ab68fddf7ba 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1968,18 +1968,6 @@ static int carl9170_parse_eeprom(struct ar9170 *ar) return -ENOMEM; ar->num_channels = chans; - /* - * I measured this, a bandswitch takes roughly - * 135 ms and a frequency switch about 80. - * - * FIXME: measure these values again once EEPROM settings - * are used, that will influence them! - */ - if (bands == 2) - ar->hw->channel_change_time = 135 * 1000; - else - ar->hw->channel_change_time = 80 * 1000; - regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]); /* second part of wiphy init */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index e71ce8c842a2..925034b80e9c 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -1071,7 +1071,6 @@ static int ieee_hw_init(struct ieee80211_hw *hw) hw->max_rates = 2; /* Primary rate and 1 fallback rate */ /* channel change time is dependent on chip and band */ - hw->channel_change_time = 7 * 1000; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_ADHOC); diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 090f01577dd2..1be819dfac63 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -302,7 +302,6 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; - hw->channel_change_time = 1000; /* TODO: find actual value */ hw->queues = 4; priv->rts_threshold = -1; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 9e7d7df03a47..59c65c37bccd 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2341,7 +2341,6 @@ static int __init init_mac80211_hwsim(void) INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); - hw->channel_change_time = 1; hw->queues = 5; hw->offchannel_tx_hw_queue = 4; hw->wiphy->interface_modes = diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index b953ad621e0b..721c738a7dd8 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5893,8 +5893,6 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0; - hw->channel_change_time = 10; - hw->queues = MWL8K_TX_WMM_QUEUES; /* Set rssi values to dBm */ diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 067e6f2fd050..b61fefad2205 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -757,7 +757,6 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT); - dev->channel_change_time = 1000; /* TODO: find actual value */ priv->beacon_req_id = cpu_to_le32(0); priv->tx_stats[P54_QUEUE_BEACON].limit = 1; priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index fcf9b621918c..2cfb6d4514cd 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -353,7 +353,6 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw) /* TODO: Correct this value for our hw */ /* TODO: define these hard code value */ - hw->channel_change_time = 100; hw->max_listen_interval = 10; hw->max_rate_tries = 4; /* hw->max_rates = 1; */ diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 3291ffa95273..ee4ec4e5660d 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -1347,7 +1347,6 @@ int wl1251_init_ieee80211(struct wl1251 *wl) /* unit us */ /* FIXME: find a proper value */ - wl->hw->channel_change_time = 10000; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e9da47cead58..18a009e593c7 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5710,7 +5710,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) /* unit us */ /* FIXME: find a proper value */ - wl->hw->channel_change_time = 10000; wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 07891a3e316e..0d29624416c3 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -788,7 +788,6 @@ static int wb35_probe(struct usb_interface *intf, dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - dev->channel_change_time = 1000; dev->max_signal = 100; dev->queues = 1; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f838af816b56..25b18877747f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1616,8 +1616,6 @@ enum ieee80211_hw_flags { * @extra_beacon_tailroom: tailroom to reserve in each beacon tx skb. * Can be used by drivers to add extra IEs. * - * @channel_change_time: time (in microseconds) it takes to change channels. - * * @max_signal: Maximum value for signal (rssi) in RX information, used * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB * @@ -1699,7 +1697,6 @@ struct ieee80211_hw { u32 flags; unsigned int extra_tx_headroom; unsigned int extra_beacon_tailroom; - int channel_change_time; int vif_data_size; int sta_data_size; int chanctx_data_size; From bb55dc2ae4367b8f711d43a2f8668a6ed42c4fd3 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 8 Jan 2014 10:52:27 -0800 Subject: [PATCH 040/121] NFC: nfcmrvl: Fix possible memory leak issue This patch fixes memory leaks in the error paths of nfcmrvl_nci_register_dev() routine. Reported-by: Dan Carpenter Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 5f91d4571429..85e8bcf98693 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -112,7 +112,8 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0); if (!priv->ndev) { nfc_err(dev, "nci_allocate_device failed"); - return ERR_PTR(-ENOMEM); + rc = -ENOMEM; + goto error; } nci_set_drvdata(priv->ndev, priv); @@ -121,11 +122,15 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, if (rc) { nfc_err(dev, "nci_register_device failed %d", rc); nci_free_device(priv->ndev); - return ERR_PTR(rc); + goto error; } nfc_info(dev, "registered with nci successfully\n"); return priv; + +error: + kfree(priv); + return ERR_PTR(rc); } EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev); From bdfbec2d2d240e9c528caae9c743801629b60166 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 9 Jan 2014 11:37:23 +0200 Subject: [PATCH 041/121] cfg80211: Add a function to get the number of supported channels Add a utility function to get the number of channels supported by the device, and update the places in the code that need this data. Signed-off-by: Ilan Peer [replace another occurrence in libertas, fix kernel-doc, fix bugs] Signed-off-by: Johannes Berg --- drivers/net/wireless/libertas/cfg.c | 7 +------ include/net/cfg80211.h | 8 ++++++++ net/wireless/nl80211.c | 13 ++----------- net/wireless/scan.c | 7 ++----- net/wireless/sme.c | 13 +++---------- net/wireless/util.c | 13 +++++++++++++ 6 files changed, 29 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 116f4aba08d6..32f75007a825 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1268,14 +1268,9 @@ static struct cfg80211_scan_request * _new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) { struct cfg80211_scan_request *creq = NULL; - int i, n_channels = 0; + int i, n_channels = ieee80211_get_num_supported_channels(wiphy); enum ieee80211_band band; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + n_channels * sizeof(void *), GFP_ATOMIC); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 56c597793d6d..b1f84b05c67e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4640,6 +4640,14 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, */ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp); +/** + * ieee80211_get_num_supported_channels - get number of channels device has + * @wiphy: the wiphy + * + * Return: the number of channels supported by the device. + */ +unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 20857126f742..d0afd82ebd77 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5285,12 +5285,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto unlock; } } else { - enum ieee80211_band band; - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; + n_channels = ieee80211_get_num_supported_channels(wiphy); } if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) @@ -5498,11 +5493,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (!n_channels) return -EINVAL; } else { - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; + n_channels = ieee80211_get_num_supported_channels(wiphy); } if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index a32d52a04c27..b528e31da2cf 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1089,11 +1089,8 @@ int cfg80211_wext_siwscan(struct net_device *dev, /* Determine number of channels, needed to allocate creq */ if (wreq && wreq->num_channels) n_channels = wreq->num_channels; - else { - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } + else + n_channels = ieee80211_get_num_supported_channels(wiphy); creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + n_channels * sizeof(void *), diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 3f64202358f4..c854f1ce22db 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -70,18 +70,11 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) if (rdev->scan_req) return -EBUSY; - if (wdev->conn->params.channel) { + if (wdev->conn->params.channel) n_channels = 1; - } else { - enum ieee80211_band band; - n_channels = 0; + else + n_channels = ieee80211_get_num_supported_channels(wdev->wiphy); - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!wdev->wiphy->bands[band]) - continue; - n_channels += wdev->wiphy->bands[band]->n_channels; - } - } request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + sizeof(request->channels[0]) * n_channels, GFP_KERNEL); diff --git a/net/wireless/util.c b/net/wireless/util.c index 329b0efb3ded..d39c37104ae2 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1481,6 +1481,19 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, return 0; } +unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy) +{ + enum ieee80211_band band; + unsigned int n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + + return n_channels; +} +EXPORT_SYMBOL(ieee80211_get_num_supported_channels); + /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ const unsigned char rfc1042_header[] __aligned(2) = From f5aa0d21dd5ada040ff42b4d40357285e4ace441 Mon Sep 17 00:00:00 2001 From: Ujjal Roy Date: Thu, 9 Jan 2014 21:16:14 +0530 Subject: [PATCH 042/121] cfg80211: add sanity check for retry limit in wext-compat Block setting the wrong values through iwconfig retry command. Add sanity checking before sending the retry limit to the driver. Signed-off-by: Ujjal Roy Signed-off-by: Johannes Berg --- net/wireless/wext-compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 051d961d4e28..5661a54ac7ee 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -370,7 +370,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev, u8 oshort = wdev->wiphy->retry_short; int err; - if (retry->disabled || + if (retry->disabled || retry->value < 1 || retry->value > 255 || (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) return -EINVAL; From 852c0153df5082311f50c062275813905f39f56e Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Thu, 9 Jan 2014 15:22:57 -0800 Subject: [PATCH 043/121] ieee80211: add definition for TDLS wide band extended capability Seventh bit of 8th byte of extended capabilities specifies wide bandwidth support for TDLS links. Add this definition to ieee80211. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 776cbb80d098..b382c7c49d84 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1902,6 +1902,7 @@ enum ieee80211_tdls_actioncode { #define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6) #define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6) +#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(7) /* TDLS specific payload type in the LLC/SNAP header */ #define WLAN_TDLS_SNAP_RFTYPE 0x2 From 0a1cb80975b67e29d572b28c1621203d1d74f4d3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 9 Jan 2014 11:05:31 +0100 Subject: [PATCH 044/121] mac80211: fix PS-Poll driver release TID Using ffs() for the PS-Poll release TID is wrong, it will cause frames to be released in order 0 1 2 3 4 5 6 7 instead of the correct 7 6 5 4 3 0 2 1. Fix this by adding a new function that implements "highest priority TID" properly. Reported-by: Eliad Peller Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 703081d04e88..93bfd6700cbf 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1227,6 +1227,17 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); } +static int find_highest_prio_tid(unsigned long tids) +{ + /* lower 3 TIDs aren't ordered perfectly */ + if (tids & 0xF8) + return fls(tids) - 1; + /* TID 0 is BE just like TID 3 */ + if (tids & BIT(0)) + return 0; + return fls(tids) - 1; +} + static void ieee80211_sta_ps_deliver_response(struct sta_info *sta, int n_frames, u8 ignored_acs, @@ -1288,7 +1299,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, hweight16(driver_release_tids) > 1) { more_data = true; driver_release_tids = - BIT(ffs(driver_release_tids) - 1); + BIT(find_highest_prio_tid( + driver_release_tids)); break; } } From f9f760b4883d7fbfb463a67267e2be6b440d1aeb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Jan 2014 17:45:07 +0100 Subject: [PATCH 045/121] mac80211: release multiple ACs in uAPSD, fix more-data bug When a response for PS-Poll or a uAPSD trigger frame is sent, the more-data bit should be set according to 802.11-2012 11.2.1.5 h), meaning that it should indicate more data on the relevant ACs (delivery-enabled or nondelivery-enabled for uAPSD or PS-Poll.) In, for example, the following scenario: * 1 frame on VO queue (either in driver or in mac80211) * at least 1 frame on VI queue (in the driver) * both VO/VI are delivery-enabled * uAPSD trigger frame received The more-data flag to the driver would not be set, even though it should be. While fixing this, I noticed that we should really release frames from multiple ACs where there's data buffered in the driver for the corresponding TIDs. To address all this, restructure the code a bit to consider all ACs if we only release driver frames or only buffered frames. This also addresses the more-data bug described above as now the TIDs will all be marked as released, so the driver will have to check the number of frames. While at it, clarify some code and comments and remove the found variable, replacing it with the appropriate sw/hw release check. Reported-by: Eliad Peller Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 82 ++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 93bfd6700cbf..93e2157a5b7b 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1245,7 +1245,6 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - bool found = false; bool more_data = false; int ac; unsigned long driver_release_tids = 0; @@ -1256,9 +1255,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, __skb_queue_head_init(&frames); - /* - * Get response frame(s) and more data bit for it. - */ + /* Get response frame(s) and more data bit for the last one. */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { unsigned long tids; @@ -1267,33 +1264,17 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, tids = ieee80211_tids_for_ac(ac); - if (!found) { - driver_release_tids = sta->driver_buffered_tids & tids; - if (driver_release_tids) { - found = true; - } else { - struct sk_buff *skb; + /* if we already have frames from software, then we can't also + * release from hardware queues + */ + if (skb_queue_empty(&frames)) + driver_release_tids |= sta->driver_buffered_tids & tids; - while (n_frames > 0) { - skb = skb_dequeue(&sta->tx_filtered[ac]); - if (!skb) { - skb = skb_dequeue( - &sta->ps_tx_buf[ac]); - if (skb) - local->total_ps_buffered--; - } - if (!skb) - break; - n_frames--; - found = true; - __skb_queue_tail(&frames, skb); - } - } - - /* - * If the driver has data on more than one TID then + if (driver_release_tids) { + /* If the driver has data on more than one TID then * certainly there's more data if we release just a - * single frame now (from a single TID). + * single frame now (from a single TID). This will + * only happen for PS-Poll. */ if (reason == IEEE80211_FRAME_RELEASE_PSPOLL && hweight16(driver_release_tids) > 1) { @@ -1303,8 +1284,28 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, driver_release_tids)); break; } + } else { + struct sk_buff *skb; + + while (n_frames > 0) { + skb = skb_dequeue(&sta->tx_filtered[ac]); + if (!skb) { + skb = skb_dequeue( + &sta->ps_tx_buf[ac]); + if (skb) + local->total_ps_buffered--; + } + if (!skb) + break; + n_frames--; + __skb_queue_tail(&frames, skb); + } } + /* If we have more frames buffered on this AC, then set the + * more-data bit and abort the loop since we can't send more + * data from other ACs before the buffered frames from this. + */ if (!skb_queue_empty(&sta->tx_filtered[ac]) || !skb_queue_empty(&sta->ps_tx_buf[ac])) { more_data = true; @@ -1312,7 +1313,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, } } - if (!found) { + if (skb_queue_empty(&frames) && !driver_release_tids) { int tid; /* @@ -1334,10 +1335,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, tid = 7 - ((ffs(~ignored_acs) - 1) << 1); ieee80211_send_null_response(sdata, sta, tid, reason); - return; - } - - if (!driver_release_tids) { + } else if (!driver_release_tids) { struct sk_buff_head pending; struct sk_buff *skb; int num = 0; @@ -1403,12 +1401,12 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* * We need to release a frame that is buffered somewhere in the * driver ... it'll have to handle that. - * Note that, as per the comment above, it'll also have to see - * if there is more than just one frame on the specific TID that - * we're releasing from, and it needs to set the more-data bit - * accordingly if we tell it that there's no more data. If we do - * tell it there's more data, then of course the more-data bit - * needs to be set anyway. + * Note that the driver also has to check the number of frames + * on the TIDs we're releasing from - if there are more than + * n_frames it has to set the more-data bit (if we didn't ask + * it to set it anyway due to other buffered frames); if there + * are fewer than n_frames it has to make sure to adjust that + * to allow the service period to end properly. */ drv_release_buffered_frames(local, sta, driver_release_tids, n_frames, reason, more_data); @@ -1416,9 +1414,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* * Note that we don't recalculate the TIM bit here as it would * most likely have no effect at all unless the driver told us - * that the TID became empty before returning here from the + * that the TID(s) became empty before returning here from the * release function. - * Either way, however, when the driver tells us that the TID + * Either way, however, when the driver tells us that the TID(s) * became empty we'll do the TIM recalculation. */ } From 03c8c06f2d080b841ecbfc63253228ba6efcab08 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 9 Jan 2014 01:45:28 +0100 Subject: [PATCH 046/121] mac80211: reset TX info flags when frame will be reprocessed The temporary TX info flags need to be cleared if the frame will be processed through the TX handlers again, otherwise it can get messed up. This fixes a bug that happened when an aggregation session was stopped while the station was sleeping - some frames might get transmitted marked as aggregation erroneously without this fix. Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 377cf974d97d..9f3cbf14989b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -500,6 +500,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) info->control.jiffies = jiffies; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS; skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb); if (!timer_pending(&local->sta_cleanup)) @@ -1073,6 +1074,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, queued = true; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS; __skb_queue_tail(&tid_tx->pending, skb); if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER) purge_skb = __skb_dequeue(&tid_tx->pending); From b77cf4f8e1892e192ec52df5dd8c158b300fc496 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 9 Jan 2014 00:00:38 +0100 Subject: [PATCH 047/121] mac80211: handle MMPDUs at EOSP correctly If a uAPSD service period ends with an MMPDU, we currently just send that MMPDU, but it obviously won't get the EOSP bit set as it doesn't have a QoS header. This contradicts the standard, so add a QoS-nulldata frame after the MMPDU to properly terminate the service period with a frame that has EOSP set. Also fix a bug wrt. the TID for the MMPDU, it shouldn't be set to 0 unconditionally but use the actual TID that was assigned. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++ net/mac80211/sta_info.c | 74 ++++++++++++++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 25b18877747f..f4ab2fb4d50c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2119,6 +2119,11 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) * and also take care of the EOSP and MORE_DATA bits in the frame. * The driver may also use ieee80211_sta_eosp() in this case. + * + * Note that if the driver ever buffers frames other than QoS-data + * frames, it must take care to never send a non-QoS-data frame as + * the last frame in a service period, adding a QoS-nulldata frame + * after a non-QoS-data frame if needed. */ /** diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 93e2157a5b7b..decd30c1e290 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1153,7 +1153,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, int tid, - enum ieee80211_frame_release_type reason) + enum ieee80211_frame_release_type reason, + bool call_driver) { struct ieee80211_local *local = sdata->local; struct ieee80211_qos_hdr *nullfunc; @@ -1211,7 +1212,9 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; - drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); + if (call_driver) + drv_allow_buffered_frames(local, sta, BIT(tid), 1, + reason, false); skb->dev = sdata->dev; @@ -1334,12 +1337,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* This will evaluate to 1, 3, 5 or 7. */ tid = 7 - ((ffs(~ignored_acs) - 1) << 1); - ieee80211_send_null_response(sdata, sta, tid, reason); + ieee80211_send_null_response(sdata, sta, tid, reason, true); } else if (!driver_release_tids) { struct sk_buff_head pending; struct sk_buff *skb; int num = 0; u16 tids = 0; + bool need_null = false; skb_queue_head_init(&pending); @@ -1373,22 +1377,57 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, ieee80211_is_qos_nullfunc(hdr->frame_control)) qoshdr = ieee80211_get_qos_ctl(hdr); - /* end service period after last frame */ - if (skb_queue_empty(&frames)) { - if (reason == IEEE80211_FRAME_RELEASE_UAPSD && - qoshdr) - *qoshdr |= IEEE80211_QOS_CTL_EOSP; + tids |= BIT(skb->priority); + + __skb_queue_tail(&pending, skb); + + /* end service period after last frame or add one */ + if (!skb_queue_empty(&frames)) + continue; + + if (reason != IEEE80211_FRAME_RELEASE_UAPSD) { + /* for PS-Poll, there's only one frame */ + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + break; + } + + /* For uAPSD, things are a bit more complicated. If the + * last frame has a QoS header (i.e. is a QoS-data or + * QoS-nulldata frame) then just set the EOSP bit there + * and be done. + * If the frame doesn't have a QoS header (which means + * it should be a bufferable MMPDU) then we can't set + * the EOSP bit in the QoS header; add a QoS-nulldata + * frame to the list to send it after the MMPDU. + * + * Note that this code is only in the mac80211-release + * code path, we assume that the driver will not buffer + * anything but QoS-data frames, or if it does, will + * create the QoS-nulldata frame by itself if needed. + * + * Cf. 802.11-2012 10.2.1.10 (c). + */ + if (qoshdr) { + *qoshdr |= IEEE80211_QOS_CTL_EOSP; info->flags |= IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; + } else { + /* The standard isn't completely clear on this + * as it says the more-data bit should be set + * if there are more BUs. The QoS-Null frame + * we're about to send isn't buffered yet, we + * only create it below, but let's pretend it + * was buffered just in case some clients only + * expect more-data=0 when eosp=1. + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + need_null = true; + num++; } - - if (qoshdr) - tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); - else - tids |= BIT(0); - - __skb_queue_tail(&pending, skb); + break; } drv_allow_buffered_frames(local, sta, tids, num, @@ -1396,6 +1435,11 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, ieee80211_add_pending_skbs(local, &pending); + if (need_null) + ieee80211_send_null_response( + sdata, sta, find_highest_prio_tid(tids), + reason, false); + sta_info_recalc_tim(sta); } else { /* From baccfe6e86443c8b7ba7f21144eedfb100ecf816 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 23 Dec 2013 08:38:27 +0200 Subject: [PATCH 048/121] ath10k: add DFS_CERTIFIED option Add Kconfig option that allow DFS functionality. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 82e8088ca9b4..a6f5285235af 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -37,3 +37,10 @@ config ATH10K_TRACING ---help--- Select this to ath10k use tracing infrastructure. +config ATH10K_DFS_CERTIFIED + bool "Atheros DFS support for certified platforms" + depends on ATH10K && CFG80211_CERTIFICATION_ONUS + default n + ---help--- + This option enables DFS support for initiating radiation on + ath10k. From cdf074099babdda9dccae7d421bd5566d70e75e3 Mon Sep 17 00:00:00 2001 From: Marek Puzyniak Date: Mon, 30 Dec 2013 09:07:51 +0100 Subject: [PATCH 049/121] ath10k: disable STA kickout in FW Currently ath10k is not using STA KICKOUT firmware functionality. In order to avoid unwanted WMI_PEER_STA_KICKOUT_EVENT event this functionality should be disabled when not used. Signed-off-by: Marek Puzyniak Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 5b45f3a8ec40..2a4e7aa4611a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2205,7 +2205,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); enum wmi_sta_powersave_param param; int ret = 0; - u32 value; + u32 value, param_id; int bit; u32 vdev_param; @@ -2297,6 +2297,13 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ath10k_warn("Failed to create peer for AP: %d\n", ret); goto err_vdev_delete; } + + param_id = ar->wmi.pdev_param->sta_kickout_th; + + /* Disable STA KICKOUT functionality in FW */ + ret = ath10k_wmi_pdev_set_param(ar, param_id, 0); + if (ret) + ath10k_warn("Failed to disable STA KICKOUT\n"); } if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { From 0e759f363e9f2f1b1a7a640b15ca613e5e27dfc0 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 2 Jan 2014 14:38:33 +0100 Subject: [PATCH 050/121] ath10k: track number of existing peers To not exceed number of allowed clients (AP mode), make sure to check how many of them are already on the peers list. 10.X firmware support up to 127 peers, non-AP centric firmwares 16. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 3 +++ drivers/net/wireless/ath/ath10k/hw.h | 1 + drivers/net/wireless/ath/ath10k/mac.c | 28 +++++++++++++++++++++++--- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 035cbf6d1abe..9d8fc29b1897 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -432,6 +432,9 @@ struct ath10k { struct list_head peers; wait_queue_head_t peer_mapping_wq; + /* number of created peers; protected by data_lock */ + int num_peers; + struct work_struct offchan_tx_work; struct sk_buff_head offchan_tx_queue; struct completion offchan_tx_completed; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 9535eaa09f09..f1505a25d810 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -115,6 +115,7 @@ enum ath10k_mcast2ucast_mode { #define TARGET_10X_MAC_AGGR_DELIM 0 #define TARGET_10X_AST_SKID_LIMIT 16 #define TARGET_10X_NUM_PEERS (128 + (TARGET_10X_NUM_VDEVS)) +#define TARGET_10X_NUM_PEERS_MAX 128 #define TARGET_10X_NUM_OFFLOAD_PEERS 0 #define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0 #define TARGET_10X_NUM_PEER_KEYS 2 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2a4e7aa4611a..ce0f1db24731 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -332,6 +332,9 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) ath10k_warn("Failed to wait for created wmi peer: %i\n", ret); return ret; } + spin_lock_bh(&ar->data_lock); + ar->num_peers++; + spin_unlock_bh(&ar->data_lock); return 0; } @@ -377,6 +380,10 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) if (ret) return ret; + spin_lock_bh(&ar->data_lock); + ar->num_peers--; + spin_unlock_bh(&ar->data_lock); + return 0; } @@ -396,6 +403,7 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) list_del(&peer->list); kfree(peer); + ar->num_peers--; } spin_unlock_bh(&ar->data_lock); } @@ -411,6 +419,7 @@ static void ath10k_peer_cleanup_all(struct ath10k *ar) list_del(&peer->list); kfree(peer); } + ar->num_peers = 0; spin_unlock_bh(&ar->data_lock); } @@ -2849,6 +2858,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int max_num_peers; int ret = 0; mutex_lock(&ar->conf_mutex); @@ -2859,9 +2869,21 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * New station addition. */ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1; + else + max_num_peers = TARGET_NUM_PEERS; + + if (ar->num_peers >= max_num_peers) { + ath10k_warn("Number of peers exceeded: peers number %d (max peers %d)\n", + ar->num_peers, max_num_peers); + ret = -ENOBUFS; + goto exit; + } + ath10k_dbg(ATH10K_DBG_MAC, - "mac vdev %d peer create %pM (new sta)\n", - arvif->vdev_id, sta->addr); + "mac vdev %d peer create %pM (new sta) num_peers %d\n", + arvif->vdev_id, sta->addr, ar->num_peers); ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr); if (ret) @@ -2911,7 +2933,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, ath10k_warn("Failed to disassociate station: %pM\n", sta->addr); } - +exit: mutex_unlock(&ar->conf_mutex); return ret; } From 2fe5288c11dae71c178a9d948347933b0b76a5f5 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 3 Jan 2014 12:59:20 +0200 Subject: [PATCH 051/121] ath10k: handle WMI debug print events Firmware can send simple ascii strings as debug messages using WMI_DEBUG_PRINT_EVENTID, print those with ATH10K_DBG_WMI log level. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 33 +++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 3ec6c9a84e84..f9925708f588 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -16,6 +16,7 @@ */ #include +#include #include "core.h" #include "htc.h" @@ -1676,9 +1677,37 @@ static void ath10k_wmi_event_profile_match(struct ath10k *ar, } static void ath10k_wmi_event_debug_print(struct ath10k *ar, - struct sk_buff *skb) + struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n"); + char buf[101], c; + int i; + + for (i = 0; i < sizeof(buf) - 1; i++) { + if (i >= skb->len) + break; + + c = skb->data[i]; + + if (c == '\0') + break; + + if (isascii(c) && isprint(c)) + buf[i] = c; + else + buf[i] = '.'; + } + + if (i == sizeof(buf) - 1) + ath10k_warn("wmi debug print truncated: %d\n", skb->len); + + /* for some reason the debug prints end with \n, remove that */ + if (skb->data[i - 1] == '\n') + i--; + + /* the last byte is always reserved for the null character */ + buf[i] = '\0'; + + ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf); } static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) From 869526b958f59592c46086c11f638824b08d164a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 3 Jan 2014 12:59:26 +0200 Subject: [PATCH 052/121] ath10k: add trace event for WMI_DEBUG_MESG_EVENTID Send firmware WMI debug logs to user space for further processing. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/trace.h | 21 +++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.c | 9 +++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index 90817ddc92ba..4eb2ecbc06ef 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -182,6 +182,27 @@ TRACE_EVENT(ath10k_htt_stats, ) ); +TRACE_EVENT(ath10k_wmi_dbglog, + TP_PROTO(void *buf, size_t buf_len), + + TP_ARGS(buf, buf_len), + + TP_STRUCT__entry( + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "len %zu", + __entry->buf_len + ) +); + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ /* we don't want to use include/trace/events */ diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index f9925708f588..4e043bb6234b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1071,9 +1071,14 @@ static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); } -static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n"); + ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug mesg len %d\n", + skb->len); + + trace_ath10k_wmi_dbglog(skb->data, skb->len); + + return 0; } static void ath10k_wmi_event_update_stats(struct ath10k *ar, From f118a3e515fd713a0ade315018f6b645bc98916e Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 3 Jan 2014 12:59:31 +0200 Subject: [PATCH 053/121] ath10k: add debugfs file to control firmware dbglog Firmware dbglogs can be now enabled through fw_dbglog file. To enable all possible log messages run: echo 0xffffffff > /sys/kernel/debug/ieee80211/phy0/ath10k/fw_dbglog And to put back firmare defaults use 0x0: echo 0x0 > /sys/kernel/debug/ieee80211/phy0/ath10k/fw_dbglog Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 2 + drivers/net/wireless/ath/ath10k/debug.c | 66 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.c | 37 ++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.h | 49 ++++++++++++++++++ 4 files changed, 154 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 9d8fc29b1897..3721c43e2941 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -272,6 +272,8 @@ struct ath10k_debug { struct delayed_work htt_stats_dwork; struct ath10k_dfs_stats dfs_stats; struct ath_dfs_pool_stats dfs_pool_stats; + + u32 fw_dbglog_mask; }; enum ath10k_state { diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 6bdfad3144af..6debd281350a 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -614,6 +614,61 @@ static const struct file_operations fops_htt_stats_mask = { .llseek = default_llseek, }; +static ssize_t ath10k_read_fw_dbglog(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned int len; + char buf[32]; + + len = scnprintf(buf, sizeof(buf), "0x%08x\n", + ar->debug.fw_dbglog_mask); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_write_fw_dbglog(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long mask; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &mask); + if (ret) + return ret; + + mutex_lock(&ar->conf_mutex); + + ar->debug.fw_dbglog_mask = mask; + + if (ar->state == ATH10K_STATE_ON) { + ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); + if (ret) { + ath10k_warn("dbglog cfg failed from debugfs: %d\n", + ret); + goto exit; + } + } + + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_fw_dbglog = { + .read = ath10k_read_fw_dbglog, + .write = ath10k_write_fw_dbglog, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_start(struct ath10k *ar) { int ret; @@ -625,6 +680,14 @@ int ath10k_debug_start(struct ath10k *ar) /* continue normally anyway, this isn't serious */ ath10k_warn("failed to start htt stats workqueue: %d\n", ret); + if (ar->debug.fw_dbglog_mask) { + ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); + if (ret) + /* not serious */ + ath10k_warn("failed to enable dbglog during start: %d", + ret); + } + return 0; } @@ -747,6 +810,9 @@ int ath10k_debug_create(struct ath10k *ar) debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_htt_stats_mask); + debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_fw_dbglog); + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) { debugfs_create_file("dfs_simulate_radar", S_IWUSR, ar->debug.debugfs_phy, ar, diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4e043bb6234b..712a606a080a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3502,3 +3502,40 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, type, delay_ms); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); } + +int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) +{ + struct wmi_dbglog_cfg_cmd *cmd; + struct sk_buff *skb; + u32 cfg; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_dbglog_cfg_cmd *)skb->data; + + if (module_enable) { + cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE, + ATH10K_DBGLOG_CFG_LOG_LVL); + } else { + /* set back defaults, all modules with WARN level */ + cfg = SM(ATH10K_DBGLOG_LEVEL_WARN, + ATH10K_DBGLOG_CFG_LOG_LVL); + module_enable = ~0; + } + + cmd->module_enable = __cpu_to_le32(module_enable); + cmd->module_valid = __cpu_to_le32(~0); + cmd->config_enable = __cpu_to_le32(cfg); + cmd->config_valid = __cpu_to_le32(ATH10K_DBGLOG_CFG_LOG_LVL_MASK); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi dbglog cfg modules %08x %08x config %08x %08x\n", + __le32_to_cpu(cmd->module_enable), + __le32_to_cpu(cmd->module_valid), + __le32_to_cpu(cmd->config_enable), + __le32_to_cpu(cmd->config_valid)); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 0087d699b85b..4aa1489f4eb2 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4090,6 +4090,54 @@ struct wmi_force_fw_hang_cmd { __le32 delay_ms; } __packed; +enum ath10k_dbglog_level { + ATH10K_DBGLOG_LEVEL_VERBOSE = 0, + ATH10K_DBGLOG_LEVEL_INFO = 1, + ATH10K_DBGLOG_LEVEL_WARN = 2, + ATH10K_DBGLOG_LEVEL_ERR = 3, +}; + +/* VAP ids to enable dbglog */ +#define ATH10K_DBGLOG_CFG_VAP_LOG_LSB 0 +#define ATH10K_DBGLOG_CFG_VAP_LOG_MASK 0x0000ffff + +/* to enable dbglog in the firmware */ +#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_LSB 16 +#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_MASK 0x00010000 + +/* timestamp resolution */ +#define ATH10K_DBGLOG_CFG_RESOLUTION_LSB 17 +#define ATH10K_DBGLOG_CFG_RESOLUTION_MASK 0x000E0000 + +/* number of queued messages before sending them to the host */ +#define ATH10K_DBGLOG_CFG_REPORT_SIZE_LSB 20 +#define ATH10K_DBGLOG_CFG_REPORT_SIZE_MASK 0x0ff00000 + +/* + * Log levels to enable. This defines the minimum level to enable, this is + * not a bitmask. See enum ath10k_dbglog_level for the values. + */ +#define ATH10K_DBGLOG_CFG_LOG_LVL_LSB 28 +#define ATH10K_DBGLOG_CFG_LOG_LVL_MASK 0x70000000 + +/* + * Note: this is a cleaned up version of a struct firmware uses. For + * example, config_valid was hidden inside an array. + */ +struct wmi_dbglog_cfg_cmd { + /* bitmask to hold mod id config*/ + __le32 module_enable; + + /* see ATH10K_DBGLOG_CFG_ */ + __le32 config_enable; + + /* mask of module id bits to be changed */ + __le32 module_valid; + + /* mask of config bits to be changed, see ATH10K_DBGLOG_CFG_ */ + __le32 config_valid; +} __packed; + #define ATH10K_RTS_MAX 2347 #define ATH10K_FRAGMT_THRESHOLD_MIN 540 #define ATH10K_FRAGMT_THRESHOLD_MAX 2346 @@ -4167,5 +4215,6 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); int ath10k_wmi_force_fw_hang(struct ath10k *ar, enum wmi_force_fw_hang_type type, u32 delay_ms); int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable); #endif /* _WMI_H_ */ From 51ab1a0a09a894442e8b091d57222c5425b6d700 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 8 Jan 2014 09:08:33 +0100 Subject: [PATCH 054/121] ath10k: add set_bitrate_mask callback Add set_bitrate_mask callback. Currently ath10k HW is limited to handle only single fixed rate setting or limit number of used spatial streams. Example: iw wlanX set bitrates legacy-5 ht-mcs-5 vht-mcs-5 2:9 will setup VHT, nss=2, mcs=9 iw wlanX set bitrates legacy-5 18 ht-mcs-5 vht-mcs-5 will setup legacy, 18Mbps iw wlanX set bitrates legacy-5 ht-mcs-5 3 vht-mcs-5 will setup HT, nss=1, mcs=3 iw wlanX set bitrate legacy-5 ht-mcs-5 vht-mcs-5 1:0-9 will setup nss=1 iw wlanX set bitrate legacy-5 ht-mcs-5 vht-mcs-5 1:0-9 2:0-9 will setup nss=2 Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 3 + drivers/net/wireless/ath/ath10k/mac.c | 302 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.h | 12 + 3 files changed, 317 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 3721c43e2941..ade1781c7186 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -253,6 +253,9 @@ struct ath10k_vif { u8 bssid[ETH_ALEN]; } ibss; } u; + + u8 fixed_rate; + u8 fixed_nss; }; struct ath10k_vif_iter { diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ce0f1db24731..7aa6c4d702d6 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3339,6 +3339,307 @@ exit: return ret; } +/* Helper table for legacy fixed_rate/bitrate_mask */ +static const u8 cck_ofdm_rate[] = { + /* CCK */ + 3, /* 1Mbps */ + 2, /* 2Mbps */ + 1, /* 5.5Mbps */ + 0, /* 11Mbps */ + /* OFDM */ + 3, /* 6Mbps */ + 7, /* 9Mbps */ + 2, /* 12Mbps */ + 6, /* 18Mbps */ + 1, /* 24Mbps */ + 5, /* 36Mbps */ + 0, /* 48Mbps */ + 4, /* 54Mbps */ +}; + +/* Check if only one bit set */ +static int ath10k_check_single_mask(u32 mask) +{ + int bit; + + bit = ffs(mask); + if (!bit) + return 0; + + mask &= ~BIT(bit - 1); + if (mask) + return 2; + + return 1; +} + +static bool +ath10k_default_bitrate_mask(struct ath10k *ar, + enum ieee80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + u32 legacy = 0x00ff; + u8 ht = 0xff, i; + u16 vht = 0x3ff; + + switch (band) { + case IEEE80211_BAND_2GHZ: + legacy = 0x00fff; + vht = 0; + break; + case IEEE80211_BAND_5GHZ: + break; + default: + return false; + } + + if (mask->control[band].legacy != legacy) + return false; + + for (i = 0; i < ar->num_rf_chains; i++) + if (mask->control[band].ht_mcs[i] != ht) + return false; + + for (i = 0; i < ar->num_rf_chains; i++) + if (mask->control[band].vht_mcs[i] != vht) + return false; + + return true; +} + +static bool +ath10k_bitrate_mask_nss(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_nss) +{ + int ht_nss = 0, vht_nss = 0, i; + + /* check legacy */ + if (ath10k_check_single_mask(mask->control[band].legacy)) + return false; + + /* check HT */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + if (mask->control[band].ht_mcs[i] == 0xff) + continue; + else if (mask->control[band].ht_mcs[i] == 0x00) + break; + else + return false; + } + + ht_nss = i; + + /* check VHT */ + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + if (mask->control[band].vht_mcs[i] == 0x03ff) + continue; + else if (mask->control[band].vht_mcs[i] == 0x0000) + break; + else + return false; + } + + vht_nss = i; + + if (ht_nss > 0 && vht_nss > 0) + return false; + + if (ht_nss) + *fixed_nss = ht_nss; + else if (vht_nss) + *fixed_nss = vht_nss; + else + return false; + + return true; +} + +static bool +ath10k_bitrate_mask_correct(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + enum wmi_rate_preamble *preamble) +{ + int legacy = 0, ht = 0, vht = 0, i; + + *preamble = WMI_RATE_PREAMBLE_OFDM; + + /* check legacy */ + legacy = ath10k_check_single_mask(mask->control[band].legacy); + if (legacy > 1) + return false; + + /* check HT */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + ht += ath10k_check_single_mask(mask->control[band].ht_mcs[i]); + if (ht > 1) + return false; + + /* check VHT */ + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + vht += ath10k_check_single_mask(mask->control[band].vht_mcs[i]); + if (vht > 1) + return false; + + /* Currently we support only one fixed_rate */ + if ((legacy + ht + vht) != 1) + return false; + + if (ht) + *preamble = WMI_RATE_PREAMBLE_HT; + else if (vht) + *preamble = WMI_RATE_PREAMBLE_VHT; + + return true; +} + +static bool +ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_rate, + u8 *fixed_nss) +{ + u8 rate = 0, pream = 0, nss = 0, i; + enum wmi_rate_preamble preamble; + + /* Check if single rate correct */ + if (!ath10k_bitrate_mask_correct(mask, band, &preamble)) + return false; + + pream = preamble; + + switch (preamble) { + case WMI_RATE_PREAMBLE_CCK: + case WMI_RATE_PREAMBLE_OFDM: + i = ffs(mask->control[band].legacy) - 1; + + if (band == IEEE80211_BAND_2GHZ && i < 4) + pream = WMI_RATE_PREAMBLE_CCK; + + if (band == IEEE80211_BAND_5GHZ) + i += 4; + + if (i >= ARRAY_SIZE(cck_ofdm_rate)) + return false; + + rate = cck_ofdm_rate[i]; + break; + case WMI_RATE_PREAMBLE_HT: + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + if (mask->control[band].ht_mcs[i]) + break; + + if (i == IEEE80211_HT_MCS_MASK_LEN) + return false; + + rate = ffs(mask->control[band].ht_mcs[i]) - 1; + nss = i; + break; + case WMI_RATE_PREAMBLE_VHT: + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + if (mask->control[band].vht_mcs[i]) + break; + + if (i == NL80211_VHT_NSS_MAX) + return false; + + rate = ffs(mask->control[band].vht_mcs[i]) - 1; + nss = i; + break; + } + + *fixed_nss = nss + 1; + nss <<= 4; + pream <<= 6; + + ath10k_dbg(ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n", + pream, nss, rate); + + *fixed_rate = pream | nss | rate; + + return true; +} + +static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_rate, + u8 *fixed_nss) +{ + /* First check full NSS mask, if we can simply limit NSS */ + if (ath10k_bitrate_mask_nss(mask, band, fixed_nss)) + return true; + + /* Next Check single rate is set */ + return ath10k_bitrate_mask_rate(mask, band, fixed_rate, fixed_nss); +} + +static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, + u8 fixed_rate, + u8 fixed_nss) +{ + struct ath10k *ar = arvif->ar; + u32 vdev_param; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + if (arvif->fixed_rate == fixed_rate && + arvif->fixed_nss == fixed_nss) + goto exit; + + if (fixed_rate == WMI_FIXED_RATE_NONE) + ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n"); + + vdev_param = ar->wmi.vdev_param->fixed_rate; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + vdev_param, fixed_rate); + if (ret) { + ath10k_warn("Could not set fixed_rate param 0x%02x: %d\n", + fixed_rate, ret); + ret = -EINVAL; + goto exit; + } + + arvif->fixed_rate = fixed_rate; + + vdev_param = ar->wmi.vdev_param->nss; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + vdev_param, fixed_nss); + + if (ret) { + ath10k_warn("Could not set fixed_nss param %d: %d\n", + fixed_nss, ret); + ret = -EINVAL; + goto exit; + } + + arvif->fixed_nss = fixed_nss; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k *ar = arvif->ar; + enum ieee80211_band band = ar->hw->conf.chandef.chan->band; + u8 fixed_rate = WMI_FIXED_RATE_NONE; + u8 fixed_nss = ar->num_rf_chains; + + if (!ath10k_default_bitrate_mask(ar, band, mask)) { + if (!ath10k_get_fixed_rate_nss(mask, band, + &fixed_rate, + &fixed_nss)) + return -EINVAL; + } + + return ath10k_set_fixed_rate_param(arvif, fixed_rate, fixed_nss); +} + static const struct ieee80211_ops ath10k_ops = { .tx = ath10k_tx, .start = ath10k_start, @@ -3361,6 +3662,7 @@ static const struct ieee80211_ops ath10k_ops = { .tx_last_beacon = ath10k_tx_last_beacon, .restart_complete = ath10k_restart_complete, .get_survey = ath10k_get_survey, + .set_bitrate_mask = ath10k_set_bitrate_mask, #ifdef CONFIG_PM .suspend = ath10k_suspend, .resume = ath10k_resume, diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 4aa1489f4eb2..4b5e7d3d32b6 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3003,6 +3003,18 @@ struct wmi_vdev_install_key_arg { const void *key_data; }; +/* + * vdev fixed rate format: + * - preamble - b7:b6 - see WMI_RATE_PREMABLE_ + * - nss - b5:b4 - ss number (0 mean 1ss) + * - rate_mcs - b3:b0 - as below + * CCK: 0 - 11Mbps, 1 - 5,5Mbps, 2 - 2Mbps, 3 - 1Mbps, + * 4 - 11Mbps (s), 5 - 5,5Mbps (s), 6 - 2Mbps (s) + * OFDM: 0 - 48Mbps, 1 - 24Mbps, 2 - 12Mbps, 3 - 6Mbps, + * 4 - 54Mbps, 5 - 36Mbps, 6 - 18Mbps, 7 - 9Mbps + * HT/VHT: MCS index + */ + /* Preamble types to be used with VDEV fixed rate configuration */ enum wmi_rate_preamble { WMI_RATE_PREAMBLE_OFDM, From 28cb1749e8dcf7f2b49174ae8531e3e5be741736 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 10 Jan 2014 11:08:55 -0800 Subject: [PATCH 055/121] ieee80211: add definition for SMS4 key len Add SMS4 key length definition to ieee80211_key_len enum. It's used by WAPI. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b382c7c49d84..e526a8cecb70 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1857,6 +1857,7 @@ enum ieee80211_key_len { WLAN_KEY_LEN_CCMP = 16, WLAN_KEY_LEN_TKIP = 32, WLAN_KEY_LEN_AES_CMAC = 16, + WLAN_KEY_LEN_SMS4 = 32, }; #define IEEE80211_WEP_IV_LEN 4 From cf7a6b2d5bd73acd8e3a78cf03259489bd83498a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 21:55:14 +0100 Subject: [PATCH 056/121] mac80211_hwsim: clean up netlink exit code There's no need to print a message, and genl_unregister_family() can't really fail so remove the error message there as well. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 59c65c37bccd..03d7b1d360ae 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2180,20 +2180,14 @@ failure: static void hwsim_exit_netlink(void) { - int ret; - /* userspace test API hasn't been adjusted for multi-channel */ if (channels > 1) return; - printk(KERN_INFO "mac80211_hwsim: closing netlink\n"); /* unregister the notifier */ netlink_unregister_notifier(&hwsim_netlink_notifier); /* unregister the family */ - ret = genl_unregister_family(&hwsim_genl_family); - if (ret) - printk(KERN_DEBUG "mac80211_hwsim: " - "unregister family %i\n", ret); + genl_unregister_family(&hwsim_genl_family); } static const struct ieee80211_iface_limit hwsim_if_limits[] = { From 38ed5048fe84d6048835916f298eaa623f59ace4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:11:06 +0100 Subject: [PATCH 057/121] mac80211_hwsim: prepare for different channel support Prepare the code to support, in theory, different devices with a different number of channels supported. Right now this doesn't really change anything, but will allow for dynamic device registration in the future. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 67 ++++++++++++++------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 03d7b1d360ae..4e9422454fd7 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -332,8 +332,10 @@ struct mac80211_hwsim_data { struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)]; struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; + struct ieee80211_iface_combination if_combination; struct mac_address addresses[2]; + int channels; struct ieee80211_channel *tmp_chan; struct delayed_work roc_done; @@ -878,7 +880,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, return; } - if (channels == 1) { + if (data->channels == 1) { channel = data->channel; } else if (txi->hw_queue == 4) { channel = data->tmp_chan; @@ -1141,7 +1143,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) data->channel = conf->chandef.chan; - WARN_ON(data->channel && channels > 1); + WARN_ON(data->channel && data->channels > 1); data->power_level = conf->power_level; if (!data->started || !data->beacon_int) @@ -1700,8 +1702,7 @@ static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw, hwsim_check_chanctx_magic(ctx); } -static struct ieee80211_ops mac80211_hwsim_ops = -{ +static const struct ieee80211_ops mac80211_hwsim_ops = { .tx = mac80211_hwsim_tx, .start = mac80211_hwsim_start, .stop = mac80211_hwsim_stop, @@ -1726,6 +1727,7 @@ static struct ieee80211_ops mac80211_hwsim_ops = .set_tsf = mac80211_hwsim_set_tsf, }; +static struct ieee80211_ops mac80211_hwsim_mchan_ops; static void mac80211_hwsim_free(void) { @@ -2206,7 +2208,7 @@ static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = { { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, }; -static struct ieee80211_iface_combination hwsim_if_comb[] = { +static const struct ieee80211_iface_combination hwsim_if_comb[] = { { .limits = hwsim_if_limits, .n_limits = ARRAY_SIZE(hwsim_if_limits), @@ -2233,6 +2235,7 @@ static int __init init_mac80211_hwsim(void) struct mac80211_hwsim_data *data; struct ieee80211_hw *hw; enum ieee80211_band band; + const struct ieee80211_ops *ops; if (radios < 1 || radios > 100) return -EINVAL; @@ -2240,28 +2243,20 @@ static int __init init_mac80211_hwsim(void) if (channels < 1) return -EINVAL; - if (channels > 1) { - hwsim_if_comb[0].num_different_channels = channels; - mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan; - mac80211_hwsim_ops.cancel_hw_scan = - mac80211_hwsim_cancel_hw_scan; - mac80211_hwsim_ops.sw_scan_start = NULL; - mac80211_hwsim_ops.sw_scan_complete = NULL; - mac80211_hwsim_ops.remain_on_channel = - mac80211_hwsim_roc; - mac80211_hwsim_ops.cancel_remain_on_channel = - mac80211_hwsim_croc; - mac80211_hwsim_ops.add_chanctx = - mac80211_hwsim_add_chanctx; - mac80211_hwsim_ops.remove_chanctx = - mac80211_hwsim_remove_chanctx; - mac80211_hwsim_ops.change_chanctx = - mac80211_hwsim_change_chanctx; - mac80211_hwsim_ops.assign_vif_chanctx = - mac80211_hwsim_assign_vif_chanctx; - mac80211_hwsim_ops.unassign_vif_chanctx = - mac80211_hwsim_unassign_vif_chanctx; - } + mac80211_hwsim_mchan_ops = mac80211_hwsim_ops; + mac80211_hwsim_mchan_ops.hw_scan = mac80211_hwsim_hw_scan; + mac80211_hwsim_mchan_ops.cancel_hw_scan = mac80211_hwsim_cancel_hw_scan; + mac80211_hwsim_mchan_ops.sw_scan_start = NULL; + mac80211_hwsim_mchan_ops.sw_scan_complete = NULL; + mac80211_hwsim_mchan_ops.remain_on_channel = mac80211_hwsim_roc; + mac80211_hwsim_mchan_ops.cancel_remain_on_channel = mac80211_hwsim_croc; + mac80211_hwsim_mchan_ops.add_chanctx = mac80211_hwsim_add_chanctx; + mac80211_hwsim_mchan_ops.remove_chanctx = mac80211_hwsim_remove_chanctx; + mac80211_hwsim_mchan_ops.change_chanctx = mac80211_hwsim_change_chanctx; + mac80211_hwsim_mchan_ops.assign_vif_chanctx = + mac80211_hwsim_assign_vif_chanctx; + mac80211_hwsim_mchan_ops.unassign_vif_chanctx = + mac80211_hwsim_unassign_vif_chanctx; spin_lock_init(&hwsim_radio_lock); INIT_LIST_HEAD(&hwsim_radios); @@ -2282,7 +2277,10 @@ static int __init init_mac80211_hwsim(void) for (i = 0; i < radios; i++) { printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n", i); - hw = ieee80211_alloc_hw(sizeof(*data), &mac80211_hwsim_ops); + ops = &mac80211_hwsim_ops; + if (channels > 1) + ops = &mac80211_hwsim_mchan_ops; + hw = ieee80211_alloc_hw(sizeof(*data), ops); if (!hw) { printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw " "failed\n"); @@ -2321,15 +2319,22 @@ static int __init init_mac80211_hwsim(void) hw->wiphy->n_addresses = 2; hw->wiphy->addresses = data->addresses; - hw->wiphy->iface_combinations = hwsim_if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb); + data->channels = channels; - if (channels > 1) { + if (data->channels > 1) { hw->wiphy->max_scan_ssids = 255; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; hw->wiphy->max_remain_on_channel_duration = 1000; /* For channels > 1 DFS is not allowed */ hw->wiphy->n_iface_combinations = 1; + hw->wiphy->iface_combinations = &data->if_combination; + data->if_combination = hwsim_if_comb[0]; + data->if_combination.num_different_channels = + data->channels; + } else { + hw->wiphy->iface_combinations = hwsim_if_comb; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(hwsim_if_comb); } INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); From 3a8cc5e73f7105faf32fb8df57ba3e7b967e1982 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:26:23 +0100 Subject: [PATCH 058/121] mac80211_hwsim: remove regtest for now The regtest thing worked based on the radio loop, but with more dynamic radio registration that loop won't really exist as is. We can add it back later with proper dynamic code. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 220 -------------------------- 1 file changed, 220 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4e9422454fd7..55cdfd6a40e8 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -57,112 +57,6 @@ static bool rctbl = false; module_param(rctbl, bool, 0444); MODULE_PARM_DESC(rctbl, "Handle rate control table"); -/** - * enum hwsim_regtest - the type of regulatory tests we offer - * - * These are the different values you can use for the regtest - * module parameter. This is useful to help test world roaming - * and the driver regulatory_hint() call and combinations of these. - * If you want to do specific alpha2 regulatory domain tests simply - * use the userspace regulatory request as that will be respected as - * well without the need of this module parameter. This is designed - * only for testing the driver regulatory request, world roaming - * and all possible combinations. - * - * @HWSIM_REGTEST_DISABLED: No regulatory tests are performed, - * this is the default value. - * @HWSIM_REGTEST_DRIVER_REG_FOLLOW: Used for testing the driver regulatory - * hint, only one driver regulatory hint will be sent as such the - * secondary radios are expected to follow. - * @HWSIM_REGTEST_DRIVER_REG_ALL: Used for testing the driver regulatory - * request with all radios reporting the same regulatory domain. - * @HWSIM_REGTEST_DIFF_COUNTRY: Used for testing the drivers calling - * different regulatory domains requests. Expected behaviour is for - * an intersection to occur but each device will still use their - * respective regulatory requested domains. Subsequent radios will - * use the resulting intersection. - * @HWSIM_REGTEST_WORLD_ROAM: Used for testing the world roaming. We accomplish - * this by using a custom beacon-capable regulatory domain for the first - * radio. All other device world roam. - * @HWSIM_REGTEST_CUSTOM_WORLD: Used for testing the custom world regulatory - * domain requests. All radios will adhere to this custom world regulatory - * domain. - * @HWSIM_REGTEST_CUSTOM_WORLD_2: Used for testing 2 custom world regulatory - * domain requests. The first radio will adhere to the first custom world - * regulatory domain, the second one to the second custom world regulatory - * domain. All other devices will world roam. - * @HWSIM_REGTEST_STRICT_FOLLOW_: Used for testing strict regulatory domain - * settings, only the first radio will send a regulatory domain request - * and use strict settings. The rest of the radios are expected to follow. - * @HWSIM_REGTEST_STRICT_ALL: Used for testing strict regulatory domain - * settings. All radios will adhere to this. - * @HWSIM_REGTEST_STRICT_AND_DRIVER_REG: Used for testing strict regulatory - * domain settings, combined with secondary driver regulatory domain - * settings. The first radio will get a strict regulatory domain setting - * using the first driver regulatory request and the second radio will use - * non-strict settings using the second driver regulatory request. All - * other devices should follow the intersection created between the - * first two. - * @HWSIM_REGTEST_ALL: Used for testing every possible mix. You will need - * at least 6 radios for a complete test. We will test in this order: - * 1 - driver custom world regulatory domain - * 2 - second custom world regulatory domain - * 3 - first driver regulatory domain request - * 4 - second driver regulatory domain request - * 5 - strict regulatory domain settings using the third driver regulatory - * domain request - * 6 and on - should follow the intersection of the 3rd, 4rth and 5th radio - * regulatory requests. - */ -enum hwsim_regtest { - HWSIM_REGTEST_DISABLED = 0, - HWSIM_REGTEST_DRIVER_REG_FOLLOW = 1, - HWSIM_REGTEST_DRIVER_REG_ALL = 2, - HWSIM_REGTEST_DIFF_COUNTRY = 3, - HWSIM_REGTEST_WORLD_ROAM = 4, - HWSIM_REGTEST_CUSTOM_WORLD = 5, - HWSIM_REGTEST_CUSTOM_WORLD_2 = 6, - HWSIM_REGTEST_STRICT_FOLLOW = 7, - HWSIM_REGTEST_STRICT_ALL = 8, - HWSIM_REGTEST_STRICT_AND_DRIVER_REG = 9, - HWSIM_REGTEST_ALL = 10, -}; - -/* Set to one of the HWSIM_REGTEST_* values above */ -static int regtest = HWSIM_REGTEST_DISABLED; -module_param(regtest, int, 0444); -MODULE_PARM_DESC(regtest, "The type of regulatory test we want to run"); - -static const char *hwsim_alpha2s[] = { - "FI", - "AL", - "US", - "DE", - "JP", - "AL", -}; - -static const struct ieee80211_regdomain hwsim_world_regdom_custom_01 = { - .n_reg_rules = 4, - .alpha2 = "99", - .reg_rules = { - REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), - REG_RULE(2484-10, 2484+10, 40, 0, 20, 0), - REG_RULE(5150-10, 5240+10, 40, 0, 30, 0), - REG_RULE(5745-10, 5825+10, 40, 0, 30, 0), - } -}; - -static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = { - .n_reg_rules = 2, - .alpha2 = "99", - .reg_rules = { - REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), - REG_RULE(5725-10, 5850+10, 40, 0, 30, - NL80211_RRF_NO_IR), - } -}; - struct hwsim_vif_priv { u32 magic; u8 bssid[ETH_ALEN]; @@ -2448,74 +2342,6 @@ static int __init init_mac80211_hwsim(void) hw->max_rates = 4; hw->max_rate_tries = 11; - /* Work to be done prior to ieee80211_register_hw() */ - switch (regtest) { - case HWSIM_REGTEST_DISABLED: - case HWSIM_REGTEST_DRIVER_REG_FOLLOW: - case HWSIM_REGTEST_DRIVER_REG_ALL: - case HWSIM_REGTEST_DIFF_COUNTRY: - /* - * Nothing to be done for driver regulatory domain - * hints prior to ieee80211_register_hw() - */ - break; - case HWSIM_REGTEST_WORLD_ROAM: - if (i == 0) { - hw->wiphy->regulatory_flags |= - REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_01); - } - break; - case HWSIM_REGTEST_CUSTOM_WORLD: - hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_01); - break; - case HWSIM_REGTEST_CUSTOM_WORLD_2: - if (i == 0) { - hw->wiphy->regulatory_flags |= - REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_01); - } else if (i == 1) { - hw->wiphy->regulatory_flags |= - REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_02); - } - break; - case HWSIM_REGTEST_STRICT_ALL: - hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG; - break; - case HWSIM_REGTEST_STRICT_FOLLOW: - case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: - if (i == 0) - hw->wiphy->regulatory_flags |= - REGULATORY_STRICT_REG; - break; - case HWSIM_REGTEST_ALL: - if (i == 0) { - hw->wiphy->regulatory_flags |= - REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_01); - } else if (i == 1) { - hw->wiphy->regulatory_flags |= - REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_02); - } else if (i == 4) - hw->wiphy->regulatory_flags |= - REGULATORY_STRICT_REG; - break; - default: - break; - } - - /* give the regulatory workqueue a chance to run */ - if (regtest) - schedule_timeout_interruptible(1); err = ieee80211_register_hw(hw); if (err < 0) { printk(KERN_DEBUG "mac80211_hwsim: " @@ -2523,52 +2349,6 @@ static int __init init_mac80211_hwsim(void) goto failed_hw; } - /* Work to be done after to ieee80211_register_hw() */ - switch (regtest) { - case HWSIM_REGTEST_WORLD_ROAM: - case HWSIM_REGTEST_DISABLED: - break; - case HWSIM_REGTEST_DRIVER_REG_FOLLOW: - if (!i) - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); - break; - case HWSIM_REGTEST_DRIVER_REG_ALL: - case HWSIM_REGTEST_STRICT_ALL: - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); - break; - case HWSIM_REGTEST_DIFF_COUNTRY: - if (i < ARRAY_SIZE(hwsim_alpha2s)) - regulatory_hint(hw->wiphy, hwsim_alpha2s[i]); - break; - case HWSIM_REGTEST_CUSTOM_WORLD: - case HWSIM_REGTEST_CUSTOM_WORLD_2: - /* - * Nothing to be done for custom world regulatory - * domains after to ieee80211_register_hw - */ - break; - case HWSIM_REGTEST_STRICT_FOLLOW: - if (i == 0) - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); - break; - case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: - if (i == 0) - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); - else if (i == 1) - regulatory_hint(hw->wiphy, hwsim_alpha2s[1]); - break; - case HWSIM_REGTEST_ALL: - if (i == 2) - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); - else if (i == 3) - regulatory_hint(hw->wiphy, hwsim_alpha2s[1]); - else if (i == 4) - regulatory_hint(hw->wiphy, hwsim_alpha2s[2]); - break; - default: - break; - } - wiphy_debug(hw->wiphy, "hwaddr %pm registered\n", hw->wiphy->perm_addr); From f39c2bfa9a1e2bae726cce65d2d328652e81f0c2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:39:27 +0100 Subject: [PATCH 059/121] mac80211_hwsim: refactor radio registration In order to support dynamic radio registration in the future, refactor the actual registration into a new function with only minor cleanups. Since it had to change anyway, also clean up the init error paths. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 455 +++++++++++++------------- 1 file changed, 225 insertions(+), 230 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 55cdfd6a40e8..23fa6ee8eefe 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2122,14 +2122,219 @@ static const struct ieee80211_iface_combination hwsim_if_comb[] = { } }; -static int __init init_mac80211_hwsim(void) +static int __init mac80211_hwsim_create_radio(int idx) { - int i, err = 0; + int err; u8 addr[ETH_ALEN]; struct mac80211_hwsim_data *data; struct ieee80211_hw *hw; enum ieee80211_band band; - const struct ieee80211_ops *ops; + const struct ieee80211_ops *ops = &mac80211_hwsim_ops; + + if (channels > 1) + ops = &mac80211_hwsim_mchan_ops; + hw = ieee80211_alloc_hw(sizeof(*data), ops); + if (!hw) { + printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n"); + err = -ENOMEM; + goto failed; + } + data = hw->priv; + data->hw = hw; + + data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx); + if (IS_ERR(data->dev)) { + printk(KERN_DEBUG + "mac80211_hwsim: device_create failed (%ld)\n", + PTR_ERR(data->dev)); + err = -ENOMEM; + goto failed_drvdata; + } + data->dev->driver = &mac80211_hwsim_driver.driver; + err = device_bind_driver(data->dev); + if (err != 0) { + printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n", + err); + goto failed_hw; + } + + skb_queue_head_init(&data->pending); + + SET_IEEE80211_DEV(hw, data->dev); + memset(addr, 0, ETH_ALEN); + addr[0] = 0x02; + addr[3] = idx >> 8; + addr[4] = idx; + memcpy(data->addresses[0].addr, addr, ETH_ALEN); + memcpy(data->addresses[1].addr, addr, ETH_ALEN); + data->addresses[1].addr[0] |= 0x40; + hw->wiphy->n_addresses = 2; + hw->wiphy->addresses = data->addresses; + + data->channels = channels; + + if (data->channels > 1) { + hw->wiphy->max_scan_ssids = 255; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->max_remain_on_channel_duration = 1000; + /* For channels > 1 DFS is not allowed */ + hw->wiphy->n_iface_combinations = 1; + hw->wiphy->iface_combinations = &data->if_combination; + data->if_combination = hwsim_if_comb[0]; + data->if_combination.num_different_channels = data->channels; + } else { + hw->wiphy->iface_combinations = hwsim_if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb); + } + + INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); + INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); + + hw->queues = 5; + hw->offchannel_tx_hw_queue = 4; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_DEVICE); + + hw->flags = IEEE80211_HW_MFP_CAPABLE | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_STATIC_SMPS | + IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_WANT_MONITOR_VIF | + IEEE80211_HW_QUEUE_CONTROL | + IEEE80211_HW_SUPPORTS_HT_CCK_RATES; + if (rctbl) + hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; + + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_AP_UAPSD; + hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; + + /* ask mac80211 to reserve space for magic */ + hw->vif_data_size = sizeof(struct hwsim_vif_priv); + hw->sta_data_size = sizeof(struct hwsim_sta_priv); + hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); + + memcpy(data->channels_2ghz, hwsim_channels_2ghz, + sizeof(hwsim_channels_2ghz)); + memcpy(data->channels_5ghz, hwsim_channels_5ghz, + sizeof(hwsim_channels_5ghz)); + memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); + + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband = &data->bands[band]; + switch (band) { + case IEEE80211_BAND_2GHZ: + sband->channels = data->channels_2ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); + sband->bitrates = data->rates; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates); + break; + case IEEE80211_BAND_5GHZ: + sband->channels = data->channels_5ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); + sband->bitrates = data->rates + 4; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; + break; + default: + continue; + } + + sband->ht_cap.ht_supported = true; + sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + sband->ht_cap.ampdu_factor = 0x3; + sband->ht_cap.ampdu_density = 0x6; + memset(&sband->ht_cap.mcs, 0, + sizeof(sband->ht_cap.mcs)); + sband->ht_cap.mcs.rx_mask[0] = 0xff; + sband->ht_cap.mcs.rx_mask[1] = 0xff; + sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + hw->wiphy->bands[band] = sband; + + sband->vht_cap.vht_supported = true; + sband->vht_cap.cap = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_TXSTBC | + IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_RXSTBC_2 | + IEEE80211_VHT_CAP_RXSTBC_3 | + IEEE80211_VHT_CAP_RXSTBC_4 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + sband->vht_cap.vht_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); + sband->vht_cap.vht_mcs.tx_mcs_map = + sband->vht_cap.vht_mcs.rx_mcs_map; + } + + /* By default all radios belong to the first group */ + data->group = 1; + mutex_init(&data->mutex); + + /* Enable frame retransmissions for lossy channels */ + hw->max_rates = 4; + hw->max_rate_tries = 11; + + err = ieee80211_register_hw(hw); + if (err < 0) { + printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", + err); + goto failed_hw; + } + + wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); + + data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); + debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); + debugfs_create_file("group", 0666, data->debugfs, data, + &hwsim_fops_group); + if (data->channels == 1) + debugfs_create_file("dfs_simulate_radar", 0222, + data->debugfs, + data, &hwsim_simulate_radar); + + tasklet_hrtimer_init(&data->beacon_timer, + mac80211_hwsim_beacon, + CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); + + spin_lock_bh(&hwsim_radio_lock); + list_add_tail(&data->list, &hwsim_radios); + spin_unlock_bh(&hwsim_radio_lock); + + return 0; + +failed_hw: + device_unregister(data->dev); +failed_drvdata: + ieee80211_free_hw(hw); +failed: + return err; +} + +static int __init init_mac80211_hwsim(void) +{ + int i, err; if (radios < 1 || radios > 100) return -EINVAL; @@ -2162,256 +2367,46 @@ static int __init init_mac80211_hwsim(void) hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); if (IS_ERR(hwsim_class)) { err = PTR_ERR(hwsim_class); - goto failed_unregister_driver; + goto out_unregister_driver; } - memset(addr, 0, ETH_ALEN); - addr[0] = 0x02; - for (i = 0; i < radios; i++) { - printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n", - i); - ops = &mac80211_hwsim_ops; - if (channels > 1) - ops = &mac80211_hwsim_mchan_ops; - hw = ieee80211_alloc_hw(sizeof(*data), ops); - if (!hw) { - printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw " - "failed\n"); - err = -ENOMEM; - goto failed; - } - data = hw->priv; - data->hw = hw; - - data->dev = device_create(hwsim_class, NULL, 0, hw, - "hwsim%d", i); - if (IS_ERR(data->dev)) { - printk(KERN_DEBUG - "mac80211_hwsim: device_create failed (%ld)\n", - PTR_ERR(data->dev)); - err = -ENOMEM; - goto failed_drvdata; - } - data->dev->driver = &mac80211_hwsim_driver.driver; - err = device_bind_driver(data->dev); - if (err != 0) { - printk(KERN_DEBUG - "mac80211_hwsim: device_bind_driver failed (%d)\n", - err); - goto failed_hw; - } - - skb_queue_head_init(&data->pending); - - SET_IEEE80211_DEV(hw, data->dev); - addr[3] = i >> 8; - addr[4] = i; - memcpy(data->addresses[0].addr, addr, ETH_ALEN); - memcpy(data->addresses[1].addr, addr, ETH_ALEN); - data->addresses[1].addr[0] |= 0x40; - hw->wiphy->n_addresses = 2; - hw->wiphy->addresses = data->addresses; - - data->channels = channels; - - if (data->channels > 1) { - hw->wiphy->max_scan_ssids = 255; - hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; - hw->wiphy->max_remain_on_channel_duration = 1000; - /* For channels > 1 DFS is not allowed */ - hw->wiphy->n_iface_combinations = 1; - hw->wiphy->iface_combinations = &data->if_combination; - data->if_combination = hwsim_if_comb[0]; - data->if_combination.num_different_channels = - data->channels; - } else { - hw->wiphy->iface_combinations = hwsim_if_comb; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(hwsim_if_comb); - } - - INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); - INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); - - hw->queues = 5; - hw->offchannel_tx_hw_queue = 4; - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_P2P_DEVICE); - - hw->flags = IEEE80211_HW_MFP_CAPABLE | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_SUPPORTS_STATIC_SMPS | - IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | - IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_WANT_MONITOR_VIF | - IEEE80211_HW_QUEUE_CONTROL | - IEEE80211_HW_SUPPORTS_HT_CCK_RATES; - if (rctbl) - hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; - - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_AP_UAPSD; - hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; - - /* ask mac80211 to reserve space for magic */ - hw->vif_data_size = sizeof(struct hwsim_vif_priv); - hw->sta_data_size = sizeof(struct hwsim_sta_priv); - hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); - - memcpy(data->channels_2ghz, hwsim_channels_2ghz, - sizeof(hwsim_channels_2ghz)); - memcpy(data->channels_5ghz, hwsim_channels_5ghz, - sizeof(hwsim_channels_5ghz)); - memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); - - for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband = &data->bands[band]; - switch (band) { - case IEEE80211_BAND_2GHZ: - sband->channels = data->channels_2ghz; - sband->n_channels = - ARRAY_SIZE(hwsim_channels_2ghz); - sband->bitrates = data->rates; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates); - break; - case IEEE80211_BAND_5GHZ: - sband->channels = data->channels_5ghz; - sband->n_channels = - ARRAY_SIZE(hwsim_channels_5ghz); - sband->bitrates = data->rates + 4; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; - break; - default: - continue; - } - - sband->ht_cap.ht_supported = true; - sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; - sband->ht_cap.ampdu_factor = 0x3; - sband->ht_cap.ampdu_density = 0x6; - memset(&sband->ht_cap.mcs, 0, - sizeof(sband->ht_cap.mcs)); - sband->ht_cap.mcs.rx_mask[0] = 0xff; - sband->ht_cap.mcs.rx_mask[1] = 0xff; - sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - - hw->wiphy->bands[band] = sband; - - sband->vht_cap.vht_supported = true; - sband->vht_cap.cap = - IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | - IEEE80211_VHT_CAP_RXLDPC | - IEEE80211_VHT_CAP_SHORT_GI_80 | - IEEE80211_VHT_CAP_SHORT_GI_160 | - IEEE80211_VHT_CAP_TXSTBC | - IEEE80211_VHT_CAP_RXSTBC_1 | - IEEE80211_VHT_CAP_RXSTBC_2 | - IEEE80211_VHT_CAP_RXSTBC_3 | - IEEE80211_VHT_CAP_RXSTBC_4 | - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; - sband->vht_cap.vht_mcs.rx_mcs_map = - cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); - sband->vht_cap.vht_mcs.tx_mcs_map = - sband->vht_cap.vht_mcs.rx_mcs_map; - } - /* By default all radios are belonging to the first group */ - data->group = 1; - mutex_init(&data->mutex); - - /* Enable frame retransmissions for lossy channels */ - hw->max_rates = 4; - hw->max_rate_tries = 11; - - err = ieee80211_register_hw(hw); - if (err < 0) { - printk(KERN_DEBUG "mac80211_hwsim: " - "ieee80211_register_hw failed (%d)\n", err); - goto failed_hw; - } - - wiphy_debug(hw->wiphy, "hwaddr %pm registered\n", - hw->wiphy->perm_addr); - - data->debugfs = debugfs_create_dir("hwsim", - hw->wiphy->debugfsdir); - debugfs_create_file("ps", 0666, data->debugfs, data, - &hwsim_fops_ps); - debugfs_create_file("group", 0666, data->debugfs, data, - &hwsim_fops_group); - if (channels == 1) - debugfs_create_file("dfs_simulate_radar", 0222, - data->debugfs, - data, &hwsim_simulate_radar); - - tasklet_hrtimer_init(&data->beacon_timer, - mac80211_hwsim_beacon, - CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); - - list_add_tail(&data->list, &hwsim_radios); + err = mac80211_hwsim_create_radio(i); + if (err) + goto out_free_radios; } hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup); if (hwsim_mon == NULL) { err = -ENOMEM; - goto failed; + goto out_free_radios; } rtnl_lock(); - err = dev_alloc_name(hwsim_mon, hwsim_mon->name); - if (err < 0) - goto failed_mon; - + if (err < 0) { + rtnl_unlock(); + goto out_free_radios; + } err = register_netdevice(hwsim_mon); - if (err < 0) - goto failed_mon; - + if (err < 0) { + rtnl_unlock(); + goto out_free_mon; + } rtnl_unlock(); err = hwsim_init_netlink(); if (err < 0) - goto failed_nl; + goto out_free_mon; return 0; -failed_nl: - printk(KERN_DEBUG "mac80211_hwsim: failed initializing netlink\n"); - return err; - -failed_mon: - rtnl_unlock(); +out_free_mon: free_netdev(hwsim_mon); +out_free_radios: mac80211_hwsim_free(); - return err; - -failed_hw: - device_unregister(data->dev); -failed_drvdata: - ieee80211_free_hw(hw); -failed: - mac80211_hwsim_free(); -failed_unregister_driver: +out_unregister_driver: platform_driver_unregister(&mac80211_hwsim_driver); return err; } From e4afb603c0799fbca3351320180ec3c8fbeefa6d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:43:11 +0100 Subject: [PATCH 060/121] mac80211_hwsim: refactor radio cleanup Refactor the radio cleanup into a new function to later allow deleting a single radio from the list. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 32 +++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 23fa6ee8eefe..cca5b3ffb22c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1623,25 +1623,29 @@ static const struct ieee80211_ops mac80211_hwsim_ops = { static struct ieee80211_ops mac80211_hwsim_mchan_ops; +static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data) +{ + debugfs_remove_recursive(data->debugfs); + ieee80211_unregister_hw(data->hw); + device_release_driver(data->dev); + device_unregister(data->dev); + ieee80211_free_hw(data->hw); +} + static void mac80211_hwsim_free(void) { - struct list_head tmplist, *i, *tmp; - struct mac80211_hwsim_data *data, *tmpdata; - - INIT_LIST_HEAD(&tmplist); + struct mac80211_hwsim_data *data; spin_lock_bh(&hwsim_radio_lock); - list_for_each_safe(i, tmp, &hwsim_radios) - list_move(i, &tmplist); - spin_unlock_bh(&hwsim_radio_lock); - - list_for_each_entry_safe(data, tmpdata, &tmplist, list) { - debugfs_remove_recursive(data->debugfs); - ieee80211_unregister_hw(data->hw); - device_release_driver(data->dev); - device_unregister(data->dev); - ieee80211_free_hw(data->hw); + while ((data = list_first_entry_or_null(&hwsim_radios, + struct mac80211_hwsim_data, + list))) { + list_del(&data->list); + spin_unlock_bh(&hwsim_radio_lock); + mac80211_hwsim_destroy_radio(data); + spin_lock_bh(&hwsim_radio_lock); } + spin_unlock_bh(&hwsim_radio_lock); class_destroy(hwsim_class); } From 8133841045b81639495dc5ca1bcab45d279661ab Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:53:42 +0100 Subject: [PATCH 061/121] mac80211_hwsim: minimize rctbl module parameter usage Check the flag that the module parameter sets instead, so later radios can use different parameters. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index cca5b3ffb22c..59caa10a33f0 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -802,7 +802,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, if (control->sta) hwsim_check_sta_magic(control->sta); - if (rctbl) + if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE) ieee80211_get_tx_rates(txi->control.vif, control->sta, skb, txi->control.rates, ARRAY_SIZE(txi->control.rates)); @@ -909,7 +909,7 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, { u32 _pid = ACCESS_ONCE(wmediumd_portid); - if (rctbl) { + if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE) { struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); ieee80211_get_tx_rates(txi->control.vif, NULL, skb, txi->control.rates, @@ -946,7 +946,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, if (skb == NULL) return; info = IEEE80211_SKB_CB(skb); - if (rctbl) + if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE) ieee80211_get_tx_rates(vif, NULL, skb, info->control.rates, ARRAY_SIZE(info->control.rates)); From 2d68992b6079cd5bdfff85251c3c670ce1560d5c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:56:30 +0100 Subject: [PATCH 062/121] mac80211_hwsim: assign index from separate counter To later allow dynamic registration, assign the index for the struct device and MAC address from a new free-running counter. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 59caa10a33f0..8df3d5e8e87a 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -217,6 +217,7 @@ static const struct ieee80211_rate hwsim_rates[] = { static spinlock_t hwsim_radio_lock; static struct list_head hwsim_radios; +static int hwsim_radio_idx; struct mac80211_hwsim_data { struct list_head list; @@ -2126,7 +2127,7 @@ static const struct ieee80211_iface_combination hwsim_if_comb[] = { } }; -static int __init mac80211_hwsim_create_radio(int idx) +static int __init mac80211_hwsim_create_radio(void) { int err; u8 addr[ETH_ALEN]; @@ -2134,6 +2135,11 @@ static int __init mac80211_hwsim_create_radio(int idx) struct ieee80211_hw *hw; enum ieee80211_band band; const struct ieee80211_ops *ops = &mac80211_hwsim_ops; + int idx; + + spin_lock_bh(&hwsim_radio_lock); + idx = hwsim_radio_idx++; + spin_unlock_bh(&hwsim_radio_lock); if (channels > 1) ops = &mac80211_hwsim_mchan_ops; @@ -2375,7 +2381,7 @@ static int __init init_mac80211_hwsim(void) } for (i = 0; i < radios; i++) { - err = mac80211_hwsim_create_radio(i); + err = mac80211_hwsim_create_radio(); if (err) goto out_free_radios; } From 9ddd12af103f60fc284103740c07219d888d1aee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 23:03:02 +0100 Subject: [PATCH 063/121] mac80211_hwsim: minor netlink cleanups Use u8 pointer instead of the struct mac_address and do some other small cleanups. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 64 ++++++++++----------------- 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 8df3d5e8e87a..ca156a5ed305 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -298,18 +298,16 @@ static struct genl_family hwsim_genl_family = { /* MAC80211_HWSIM netlink policy */ static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { - [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, - .len = 6*sizeof(u8) }, - [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, - .len = 6*sizeof(u8) }, + [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, .len = ETH_ALEN }, + [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, .len = ETH_ALEN }, [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 }, [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 }, [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 }, [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC, - .len = IEEE80211_TX_MAX_RATES*sizeof( - struct hwsim_tx_rate)}, + .len = IEEE80211_TX_MAX_RATES * + sizeof(struct hwsim_tx_rate)}, [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, }; @@ -536,7 +534,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, } if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, - sizeof(struct mac_address), data->addresses[1].addr)) + ETH_ALEN, data->addresses[1].addr)) goto nla_put_failure; /* We get the skb->data */ @@ -1828,16 +1826,14 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group, hwsim_fops_group_read, hwsim_fops_group_write, "%llx\n"); -static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr( - struct mac_address *addr) +static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr) { struct mac80211_hwsim_data *data; bool _found = false; spin_lock_bh(&hwsim_radio_lock); list_for_each_entry(data, &hwsim_radios, list) { - if (memcmp(data->addresses[1].addr, addr, - sizeof(struct mac_address)) == 0) { + if (memcmp(data->addresses[1].addr, addr, ETH_ALEN) == 0) { _found = true; break; } @@ -1860,27 +1856,23 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, struct hwsim_tx_rate *tx_attempts; unsigned long ret_skb_ptr; struct sk_buff *skb, *tmp; - struct mac_address *src; + const u8 *src; unsigned int hwsim_flags; - int i; bool found = false; if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || - !info->attrs[HWSIM_ATTR_FLAGS] || - !info->attrs[HWSIM_ATTR_COOKIE] || - !info->attrs[HWSIM_ATTR_TX_INFO]) + !info->attrs[HWSIM_ATTR_FLAGS] || + !info->attrs[HWSIM_ATTR_COOKIE] || + !info->attrs[HWSIM_ATTR_TX_INFO]) goto out; - src = (struct mac_address *)nla_data( - info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); + src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]); - ret_skb_ptr = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); data2 = get_hwsim_data_ref_from_addr(src); - - if (data2 == NULL) + if (!data2) goto out; /* look for the skb matching the cookie passed back from user */ @@ -1937,9 +1929,9 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, struct mac80211_hwsim_data *data2; struct ieee80211_rx_status rx_status; - struct mac_address *dst; + const u8 *dst; int frame_data_len; - char *frame_data; + void *frame_data; struct sk_buff *skb = NULL; if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] || @@ -1948,27 +1940,23 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, !info->attrs[HWSIM_ATTR_SIGNAL]) goto out; - dst = (struct mac_address *)nla_data( - info->attrs[HWSIM_ATTR_ADDR_RECEIVER]); - + dst = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_RECEIVER]); frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]); - frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]); + frame_data = (void *)nla_data(info->attrs[HWSIM_ATTR_FRAME]); /* Allocate new skb here */ skb = alloc_skb(frame_data_len, GFP_KERNEL); if (skb == NULL) goto err; - if (frame_data_len <= IEEE80211_MAX_DATA_LEN) { - /* Copy the data */ - memcpy(skb_put(skb, frame_data_len), frame_data, - frame_data_len); - } else + if (frame_data_len > IEEE80211_MAX_DATA_LEN) goto err; - data2 = get_hwsim_data_ref_from_addr(dst); + /* Copy the data */ + memcpy(skb_put(skb, frame_data_len), frame_data, frame_data_len); - if (data2 == NULL) + data2 = get_hwsim_data_ref_from_addr(dst); + if (!data2) goto out; /* check if radio is configured properly */ @@ -1976,7 +1964,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, if (data2->idle || !data2->started) goto out; - /*A frame is received from user space*/ + /* A frame is received from user space */ memset(&rx_status, 0, sizeof(rx_status)); rx_status.freq = data2->channel->center_freq; rx_status.band = data2->channel->band; @@ -1998,18 +1986,12 @@ out: static int hwsim_register_received_nl(struct sk_buff *skb_2, struct genl_info *info) { - if (info == NULL) - goto out; - wmediumd_portid = info->snd_portid; printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, " "switching to wmediumd mode with pid %d\n", info->snd_portid); return 0; -out: - printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__); - return -EINVAL; } /* Generic Netlink operations array */ From de0421d53bfb5f7783e5228f8cf49f2ae7140d54 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 23:12:12 +0100 Subject: [PATCH 064/121] mac80211_hwsim: shuffle code to prepare for dynamic radios This will make the next patch, adding support for netlink, smaller and more readable. The code is not modified here. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 826 +++++++++++++------------- 1 file changed, 413 insertions(+), 413 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index ca156a5ed305..1c51c33c385f 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -215,10 +215,53 @@ static const struct ieee80211_rate hwsim_rates[] = { { .bitrate = 540 } }; +static const struct ieee80211_iface_limit hwsim_if_limits[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, + { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, +}; + +static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = { + { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, +}; + +static const struct ieee80211_iface_combination hwsim_if_comb[] = { + { + .limits = hwsim_if_limits, + .n_limits = ARRAY_SIZE(hwsim_if_limits), + .max_interfaces = 2048, + .num_different_channels = 1, + }, + { + .limits = hwsim_if_dfs_limits, + .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), + } +}; + static spinlock_t hwsim_radio_lock; static struct list_head hwsim_radios; static int hwsim_radio_idx; +static struct platform_driver mac80211_hwsim_driver = { + .driver = { + .name = "mac80211_hwsim", + .owner = THIS_MODULE, + }, +}; + struct mac80211_hwsim_data { struct list_head list; struct ieee80211_hw *hw; @@ -311,6 +354,161 @@ static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, }; +static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan); + +/* sysfs attributes */ +static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct sk_buff *skb; + struct ieee80211_pspoll *pspoll; + + if (!vp->assoc) + return; + + wiphy_debug(data->hw->wiphy, + "%s: send PS-Poll to %pM for aid %d\n", + __func__, vp->bssid, vp->aid); + + skb = dev_alloc_skb(sizeof(*pspoll)); + if (!skb) + return; + pspoll = (void *) skb_put(skb, sizeof(*pspoll)); + pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_PSPOLL | + IEEE80211_FCTL_PM); + pspoll->aid = cpu_to_le16(0xc000 | vp->aid); + memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); + memcpy(pspoll->ta, mac, ETH_ALEN); + + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_read_unlock(); +} + +static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, + struct ieee80211_vif *vif, int ps) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + + if (!vp->assoc) + return; + + wiphy_debug(data->hw->wiphy, + "%s: send data::nullfunc to %pM ps=%d\n", + __func__, vp->bssid, ps); + + skb = dev_alloc_skb(sizeof(*hdr)); + if (!skb) + return; + hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + (ps ? IEEE80211_FCTL_PM : 0)); + hdr->duration_id = cpu_to_le16(0); + memcpy(hdr->addr1, vp->bssid, ETH_ALEN); + memcpy(hdr->addr2, mac, ETH_ALEN); + memcpy(hdr->addr3, vp->bssid, ETH_ALEN); + + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_read_unlock(); +} + + +static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + hwsim_send_nullfunc(data, mac, vif, 1); +} + +static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + hwsim_send_nullfunc(data, mac, vif, 0); +} + +static int hwsim_fops_ps_read(void *dat, u64 *val) +{ + struct mac80211_hwsim_data *data = dat; + *val = data->ps; + return 0; +} + +static int hwsim_fops_ps_write(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + enum ps_mode old_ps; + + if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && + val != PS_MANUAL_POLL) + return -EINVAL; + + old_ps = data->ps; + data->ps = val; + + if (val == PS_MANUAL_POLL) { + ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_ps_poll, data); + data->ps_poll_pending = true; + } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { + ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_ps, + data); + } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { + ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_no_ps, + data); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, + "%llu\n"); + +static int hwsim_write_simulate_radar(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + + ieee80211_radar_detected(data->hw); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL, + hwsim_write_simulate_radar, "%llu\n"); + +static int hwsim_fops_group_read(void *dat, u64 *val) +{ + struct mac80211_hwsim_data *data = dat; + *val = data->group; + return 0; +} + +static int hwsim_fops_group_write(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + data->group = val; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group, + hwsim_fops_group_read, hwsim_fops_group_write, + "%llx\n"); + static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -1283,8 +1481,6 @@ static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, }; -static int hwsim_fops_ps_write(void *dat, u64 val); - static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len) @@ -1622,6 +1818,221 @@ static const struct ieee80211_ops mac80211_hwsim_ops = { static struct ieee80211_ops mac80211_hwsim_mchan_ops; +static int __init mac80211_hwsim_create_radio(void) +{ + int err; + u8 addr[ETH_ALEN]; + struct mac80211_hwsim_data *data; + struct ieee80211_hw *hw; + enum ieee80211_band band; + const struct ieee80211_ops *ops = &mac80211_hwsim_ops; + int idx; + + spin_lock_bh(&hwsim_radio_lock); + idx = hwsim_radio_idx++; + spin_unlock_bh(&hwsim_radio_lock); + + if (channels > 1) + ops = &mac80211_hwsim_mchan_ops; + hw = ieee80211_alloc_hw(sizeof(*data), ops); + if (!hw) { + printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n"); + err = -ENOMEM; + goto failed; + } + data = hw->priv; + data->hw = hw; + + data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx); + if (IS_ERR(data->dev)) { + printk(KERN_DEBUG + "mac80211_hwsim: device_create failed (%ld)\n", + PTR_ERR(data->dev)); + err = -ENOMEM; + goto failed_drvdata; + } + data->dev->driver = &mac80211_hwsim_driver.driver; + err = device_bind_driver(data->dev); + if (err != 0) { + printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n", + err); + goto failed_hw; + } + + skb_queue_head_init(&data->pending); + + SET_IEEE80211_DEV(hw, data->dev); + memset(addr, 0, ETH_ALEN); + addr[0] = 0x02; + addr[3] = idx >> 8; + addr[4] = idx; + memcpy(data->addresses[0].addr, addr, ETH_ALEN); + memcpy(data->addresses[1].addr, addr, ETH_ALEN); + data->addresses[1].addr[0] |= 0x40; + hw->wiphy->n_addresses = 2; + hw->wiphy->addresses = data->addresses; + + data->channels = channels; + + if (data->channels > 1) { + hw->wiphy->max_scan_ssids = 255; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->max_remain_on_channel_duration = 1000; + /* For channels > 1 DFS is not allowed */ + hw->wiphy->n_iface_combinations = 1; + hw->wiphy->iface_combinations = &data->if_combination; + data->if_combination = hwsim_if_comb[0]; + data->if_combination.num_different_channels = data->channels; + } else { + hw->wiphy->iface_combinations = hwsim_if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb); + } + + INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); + INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); + + hw->queues = 5; + hw->offchannel_tx_hw_queue = 4; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_DEVICE); + + hw->flags = IEEE80211_HW_MFP_CAPABLE | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_STATIC_SMPS | + IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_WANT_MONITOR_VIF | + IEEE80211_HW_QUEUE_CONTROL | + IEEE80211_HW_SUPPORTS_HT_CCK_RATES; + if (rctbl) + hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; + + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_AP_UAPSD; + hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; + + /* ask mac80211 to reserve space for magic */ + hw->vif_data_size = sizeof(struct hwsim_vif_priv); + hw->sta_data_size = sizeof(struct hwsim_sta_priv); + hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); + + memcpy(data->channels_2ghz, hwsim_channels_2ghz, + sizeof(hwsim_channels_2ghz)); + memcpy(data->channels_5ghz, hwsim_channels_5ghz, + sizeof(hwsim_channels_5ghz)); + memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); + + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband = &data->bands[band]; + switch (band) { + case IEEE80211_BAND_2GHZ: + sband->channels = data->channels_2ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); + sband->bitrates = data->rates; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates); + break; + case IEEE80211_BAND_5GHZ: + sband->channels = data->channels_5ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); + sband->bitrates = data->rates + 4; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; + break; + default: + continue; + } + + sband->ht_cap.ht_supported = true; + sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + sband->ht_cap.ampdu_factor = 0x3; + sband->ht_cap.ampdu_density = 0x6; + memset(&sband->ht_cap.mcs, 0, + sizeof(sband->ht_cap.mcs)); + sband->ht_cap.mcs.rx_mask[0] = 0xff; + sband->ht_cap.mcs.rx_mask[1] = 0xff; + sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + hw->wiphy->bands[band] = sband; + + sband->vht_cap.vht_supported = true; + sband->vht_cap.cap = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_TXSTBC | + IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_RXSTBC_2 | + IEEE80211_VHT_CAP_RXSTBC_3 | + IEEE80211_VHT_CAP_RXSTBC_4 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + sband->vht_cap.vht_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); + sband->vht_cap.vht_mcs.tx_mcs_map = + sband->vht_cap.vht_mcs.rx_mcs_map; + } + + /* By default all radios belong to the first group */ + data->group = 1; + mutex_init(&data->mutex); + + /* Enable frame retransmissions for lossy channels */ + hw->max_rates = 4; + hw->max_rate_tries = 11; + + err = ieee80211_register_hw(hw); + if (err < 0) { + printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", + err); + goto failed_hw; + } + + wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); + + data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); + debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); + debugfs_create_file("group", 0666, data->debugfs, data, + &hwsim_fops_group); + if (data->channels == 1) + debugfs_create_file("dfs_simulate_radar", 0222, + data->debugfs, + data, &hwsim_simulate_radar); + + tasklet_hrtimer_init(&data->beacon_timer, + mac80211_hwsim_beacon, + CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); + + spin_lock_bh(&hwsim_radio_lock); + list_add_tail(&data->list, &hwsim_radios); + spin_unlock_bh(&hwsim_radio_lock); + + return 0; + +failed_hw: + device_unregister(data->dev); +failed_drvdata: + ieee80211_free_hw(hw); +failed: + return err; +} + static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data) { debugfs_remove_recursive(data->debugfs); @@ -1648,13 +2059,6 @@ static void mac80211_hwsim_free(void) class_destroy(hwsim_class); } -static struct platform_driver mac80211_hwsim_driver = { - .driver = { - .name = "mac80211_hwsim", - .owner = THIS_MODULE, - }, -}; - static const struct net_device_ops hwsim_netdev_ops = { .ndo_start_xmit = hwsim_mon_xmit, .ndo_change_mtu = eth_change_mtu, @@ -1673,159 +2077,6 @@ static void hwsim_mon_setup(struct net_device *dev) dev->dev_addr[0] = 0x12; } - -static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - struct sk_buff *skb; - struct ieee80211_pspoll *pspoll; - - if (!vp->assoc) - return; - - wiphy_debug(data->hw->wiphy, - "%s: send PS-Poll to %pM for aid %d\n", - __func__, vp->bssid, vp->aid); - - skb = dev_alloc_skb(sizeof(*pspoll)); - if (!skb) - return; - pspoll = (void *) skb_put(skb, sizeof(*pspoll)); - pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_PSPOLL | - IEEE80211_FCTL_PM); - pspoll->aid = cpu_to_le16(0xc000 | vp->aid); - memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); - memcpy(pspoll->ta, mac, ETH_ALEN); - - rcu_read_lock(); - mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); - rcu_read_unlock(); -} - -static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, - struct ieee80211_vif *vif, int ps) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - struct sk_buff *skb; - struct ieee80211_hdr *hdr; - - if (!vp->assoc) - return; - - wiphy_debug(data->hw->wiphy, - "%s: send data::nullfunc to %pM ps=%d\n", - __func__, vp->bssid, ps); - - skb = dev_alloc_skb(sizeof(*hdr)); - if (!skb) - return; - hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN); - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC | - (ps ? IEEE80211_FCTL_PM : 0)); - hdr->duration_id = cpu_to_le16(0); - memcpy(hdr->addr1, vp->bssid, ETH_ALEN); - memcpy(hdr->addr2, mac, ETH_ALEN); - memcpy(hdr->addr3, vp->bssid, ETH_ALEN); - - rcu_read_lock(); - mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); - rcu_read_unlock(); -} - - -static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - hwsim_send_nullfunc(data, mac, vif, 1); -} - - -static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - hwsim_send_nullfunc(data, mac, vif, 0); -} - - -static int hwsim_fops_ps_read(void *dat, u64 *val) -{ - struct mac80211_hwsim_data *data = dat; - *val = data->ps; - return 0; -} - -static int hwsim_fops_ps_write(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - enum ps_mode old_ps; - - if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && - val != PS_MANUAL_POLL) - return -EINVAL; - - old_ps = data->ps; - data->ps = val; - - if (val == PS_MANUAL_POLL) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_ps_poll, data); - data->ps_poll_pending = true; - } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_ps, - data); - } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_no_ps, - data); - } - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, - "%llu\n"); - -static int hwsim_write_simulate_radar(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - - ieee80211_radar_detected(data->hw); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL, - hwsim_write_simulate_radar, "%llu\n"); - -static int hwsim_fops_group_read(void *dat, u64 *val) -{ - struct mac80211_hwsim_data *data = dat; - *val = data->group; - return 0; -} - -static int hwsim_fops_group_write(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - data->group = val; - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group, - hwsim_fops_group_read, hwsim_fops_group_write, - "%llx\n"); - static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr) { struct mac80211_hwsim_data *data; @@ -2073,257 +2324,6 @@ static void hwsim_exit_netlink(void) genl_unregister_family(&hwsim_genl_family); } -static const struct ieee80211_iface_limit hwsim_if_limits[] = { - { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, - { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | -#ifdef CONFIG_MAC80211_MESH - BIT(NL80211_IFTYPE_MESH_POINT) | -#endif - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_GO) }, - { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, -}; - -static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = { - { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, -}; - -static const struct ieee80211_iface_combination hwsim_if_comb[] = { - { - .limits = hwsim_if_limits, - .n_limits = ARRAY_SIZE(hwsim_if_limits), - .max_interfaces = 2048, - .num_different_channels = 1, - }, - { - .limits = hwsim_if_dfs_limits, - .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits), - .max_interfaces = 8, - .num_different_channels = 1, - .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80) | - BIT(NL80211_CHAN_WIDTH_160), - } -}; - -static int __init mac80211_hwsim_create_radio(void) -{ - int err; - u8 addr[ETH_ALEN]; - struct mac80211_hwsim_data *data; - struct ieee80211_hw *hw; - enum ieee80211_band band; - const struct ieee80211_ops *ops = &mac80211_hwsim_ops; - int idx; - - spin_lock_bh(&hwsim_radio_lock); - idx = hwsim_radio_idx++; - spin_unlock_bh(&hwsim_radio_lock); - - if (channels > 1) - ops = &mac80211_hwsim_mchan_ops; - hw = ieee80211_alloc_hw(sizeof(*data), ops); - if (!hw) { - printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n"); - err = -ENOMEM; - goto failed; - } - data = hw->priv; - data->hw = hw; - - data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx); - if (IS_ERR(data->dev)) { - printk(KERN_DEBUG - "mac80211_hwsim: device_create failed (%ld)\n", - PTR_ERR(data->dev)); - err = -ENOMEM; - goto failed_drvdata; - } - data->dev->driver = &mac80211_hwsim_driver.driver; - err = device_bind_driver(data->dev); - if (err != 0) { - printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n", - err); - goto failed_hw; - } - - skb_queue_head_init(&data->pending); - - SET_IEEE80211_DEV(hw, data->dev); - memset(addr, 0, ETH_ALEN); - addr[0] = 0x02; - addr[3] = idx >> 8; - addr[4] = idx; - memcpy(data->addresses[0].addr, addr, ETH_ALEN); - memcpy(data->addresses[1].addr, addr, ETH_ALEN); - data->addresses[1].addr[0] |= 0x40; - hw->wiphy->n_addresses = 2; - hw->wiphy->addresses = data->addresses; - - data->channels = channels; - - if (data->channels > 1) { - hw->wiphy->max_scan_ssids = 255; - hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; - hw->wiphy->max_remain_on_channel_duration = 1000; - /* For channels > 1 DFS is not allowed */ - hw->wiphy->n_iface_combinations = 1; - hw->wiphy->iface_combinations = &data->if_combination; - data->if_combination = hwsim_if_comb[0]; - data->if_combination.num_different_channels = data->channels; - } else { - hw->wiphy->iface_combinations = hwsim_if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb); - } - - INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); - INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); - - hw->queues = 5; - hw->offchannel_tx_hw_queue = 4; - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_P2P_DEVICE); - - hw->flags = IEEE80211_HW_MFP_CAPABLE | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_SUPPORTS_STATIC_SMPS | - IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | - IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_WANT_MONITOR_VIF | - IEEE80211_HW_QUEUE_CONTROL | - IEEE80211_HW_SUPPORTS_HT_CCK_RATES; - if (rctbl) - hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; - - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_AP_UAPSD; - hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; - - /* ask mac80211 to reserve space for magic */ - hw->vif_data_size = sizeof(struct hwsim_vif_priv); - hw->sta_data_size = sizeof(struct hwsim_sta_priv); - hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); - - memcpy(data->channels_2ghz, hwsim_channels_2ghz, - sizeof(hwsim_channels_2ghz)); - memcpy(data->channels_5ghz, hwsim_channels_5ghz, - sizeof(hwsim_channels_5ghz)); - memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); - - for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband = &data->bands[band]; - switch (band) { - case IEEE80211_BAND_2GHZ: - sband->channels = data->channels_2ghz; - sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); - sband->bitrates = data->rates; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates); - break; - case IEEE80211_BAND_5GHZ: - sband->channels = data->channels_5ghz; - sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); - sband->bitrates = data->rates + 4; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; - break; - default: - continue; - } - - sband->ht_cap.ht_supported = true; - sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; - sband->ht_cap.ampdu_factor = 0x3; - sband->ht_cap.ampdu_density = 0x6; - memset(&sband->ht_cap.mcs, 0, - sizeof(sband->ht_cap.mcs)); - sband->ht_cap.mcs.rx_mask[0] = 0xff; - sband->ht_cap.mcs.rx_mask[1] = 0xff; - sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - - hw->wiphy->bands[band] = sband; - - sband->vht_cap.vht_supported = true; - sband->vht_cap.cap = - IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | - IEEE80211_VHT_CAP_RXLDPC | - IEEE80211_VHT_CAP_SHORT_GI_80 | - IEEE80211_VHT_CAP_SHORT_GI_160 | - IEEE80211_VHT_CAP_TXSTBC | - IEEE80211_VHT_CAP_RXSTBC_1 | - IEEE80211_VHT_CAP_RXSTBC_2 | - IEEE80211_VHT_CAP_RXSTBC_3 | - IEEE80211_VHT_CAP_RXSTBC_4 | - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; - sband->vht_cap.vht_mcs.rx_mcs_map = - cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); - sband->vht_cap.vht_mcs.tx_mcs_map = - sband->vht_cap.vht_mcs.rx_mcs_map; - } - - /* By default all radios belong to the first group */ - data->group = 1; - mutex_init(&data->mutex); - - /* Enable frame retransmissions for lossy channels */ - hw->max_rates = 4; - hw->max_rate_tries = 11; - - err = ieee80211_register_hw(hw); - if (err < 0) { - printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", - err); - goto failed_hw; - } - - wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); - - data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); - debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); - debugfs_create_file("group", 0666, data->debugfs, data, - &hwsim_fops_group); - if (data->channels == 1) - debugfs_create_file("dfs_simulate_radar", 0222, - data->debugfs, - data, &hwsim_simulate_radar); - - tasklet_hrtimer_init(&data->beacon_timer, - mac80211_hwsim_beacon, - CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); - - spin_lock_bh(&hwsim_radio_lock); - list_add_tail(&data->list, &hwsim_radios); - spin_unlock_bh(&hwsim_radio_lock); - - return 0; - -failed_hw: - device_unregister(data->dev); -failed_drvdata: - ieee80211_free_hw(hw); -failed: - return err; -} - static int __init init_mac80211_hwsim(void) { int i, err; From 85ca8fc74671def6041fb12c0a7d3423da56ffd3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jan 2014 23:21:34 +0100 Subject: [PATCH 065/121] mac80211_hwsim: verify wmediumd socket There can't be two wmediumd instances controlling hwsim, so reject registration from a second one and verify in the commands that it's the correct instance calling. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 1c51c33c385f..4ee88a9b095d 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2112,6 +2112,9 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, int i; bool found = false; + if (info->snd_portid != wmediumd_portid) + return -EINVAL; + if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || !info->attrs[HWSIM_ATTR_FLAGS] || !info->attrs[HWSIM_ATTR_COOKIE] || @@ -2185,6 +2188,9 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, void *frame_data; struct sk_buff *skb = NULL; + if (info->snd_portid != wmediumd_portid) + return -EINVAL; + if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] || !info->attrs[HWSIM_ATTR_FRAME] || !info->attrs[HWSIM_ATTR_RX_RATE] || @@ -2237,6 +2243,9 @@ out: static int hwsim_register_received_nl(struct sk_buff *skb_2, struct genl_info *info) { + if (wmediumd_portid) + return -EBUSY; + wmediumd_portid = info->snd_portid; printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, " From c5ac08548b53c3292aa3cbd02de6ae99ca7271e6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jan 2014 23:28:08 +0100 Subject: [PATCH 066/121] mac80211_hwsim: register netlink even with multi-channel Reject wmediumd registrations when any devices have multi-channel capability, but register the generic netlink family unconditionally to make it possible to add new commands that shouldn't depend on the number of (default) channels. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4ee88a9b095d..5bad3d4ccdba 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2243,6 +2243,22 @@ out: static int hwsim_register_received_nl(struct sk_buff *skb_2, struct genl_info *info) { + struct mac80211_hwsim_data *data; + int chans = 1; + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) + chans = max(chans, data->channels); + spin_unlock_bh(&hwsim_radio_lock); + + /* In the future we should revise the userspace API and allow it + * to set a flag that it does support multi-channel, then we can + * let this pass conditionally on the flag. + * For current userspace, prohibit it since it won't work right. + */ + if (chans > 1) + return -EOPNOTSUPP; + if (wmediumd_portid) return -EBUSY; @@ -2300,10 +2316,6 @@ static int hwsim_init_netlink(void) { int rc; - /* userspace test API hasn't been adjusted for multi-channel */ - if (channels > 1) - return 0; - printk(KERN_INFO "mac80211_hwsim: initializing netlink\n"); rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops); @@ -2323,10 +2335,6 @@ failure: static void hwsim_exit_netlink(void) { - /* userspace test API hasn't been adjusted for multi-channel */ - if (channels > 1) - return; - /* unregister the notifier */ netlink_unregister_notifier(&hwsim_netlink_notifier); /* unregister the family */ From bc7910989601fbde02b3dd485d595a614d21ef1e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 23:29:20 +0100 Subject: [PATCH 067/121] mac80211_hwsim: allow creating/destroying radios on the fly Add new commands to the hwsim generic netlink family to allow creating and destroying radios on the fly. The number of channels a radio supports can be specified when it's created, if it isn't the module parameter will be used as default. This should be extended in the future to allow other parameters to be specified, e.g. * list of channels * interface combinations, particularly P2P_DEVICE support * regtest * and pretty much all other hardware capabilities Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 60 ++++++++++++++++++++++++--- drivers/net/wireless/mac80211_hwsim.h | 11 +++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 5bad3d4ccdba..056dae73b25c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -273,7 +273,7 @@ struct mac80211_hwsim_data { struct ieee80211_iface_combination if_combination; struct mac_address addresses[2]; - int channels; + int channels, idx; struct ieee80211_channel *tmp_chan; struct delayed_work roc_done; @@ -352,6 +352,8 @@ static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { .len = IEEE80211_TX_MAX_RATES * sizeof(struct hwsim_tx_rate)}, [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, + [HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 }, + [HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 }, }; static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, @@ -1818,7 +1820,7 @@ static const struct ieee80211_ops mac80211_hwsim_ops = { static struct ieee80211_ops mac80211_hwsim_mchan_ops; -static int __init mac80211_hwsim_create_radio(void) +static int mac80211_hwsim_create_radio(int channels) { int err; u8 addr[ETH_ALEN]; @@ -1873,6 +1875,7 @@ static int __init mac80211_hwsim_create_radio(void) hw->wiphy->addresses = data->addresses; data->channels = channels; + data->idx = idx; if (data->channels > 1) { hw->wiphy->max_scan_ssids = 255; @@ -2023,7 +2026,7 @@ static int __init mac80211_hwsim_create_radio(void) list_add_tail(&data->list, &hwsim_radios); spin_unlock_bh(&hwsim_radio_lock); - return 0; + return idx; failed_hw: device_unregister(data->dev); @@ -2270,6 +2273,39 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2, return 0; } +static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info) +{ + unsigned int chans = channels; + + if (info->attrs[HWSIM_ATTR_CHANNELS]) + chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]); + + return mac80211_hwsim_create_radio(chans); +} + +static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info) +{ + struct mac80211_hwsim_data *data; + int idx; + + if (!info->attrs[HWSIM_ATTR_RADIO_ID]) + return -EINVAL; + idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]); + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) { + if (data->idx != idx) + continue; + list_del(&data->list); + spin_unlock_bh(&hwsim_radio_lock); + mac80211_hwsim_destroy_radio(data); + return 0; + } + spin_unlock_bh(&hwsim_radio_lock); + + return -ENODEV; +} + /* Generic Netlink operations array */ static const struct genl_ops hwsim_ops[] = { { @@ -2288,6 +2324,18 @@ static const struct genl_ops hwsim_ops[] = { .policy = hwsim_genl_policy, .doit = hwsim_tx_info_frame_received_nl, }, + { + .cmd = HWSIM_CMD_CREATE_RADIO, + .policy = hwsim_genl_policy, + .doit = hwsim_create_radio_nl, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = HWSIM_CMD_DESTROY_RADIO, + .policy = hwsim_genl_policy, + .doit = hwsim_destroy_radio_nl, + .flags = GENL_ADMIN_PERM, + }, }; static int mac80211_hwsim_netlink_notify(struct notifier_block *nb, @@ -2345,7 +2393,7 @@ static int __init init_mac80211_hwsim(void) { int i, err; - if (radios < 1 || radios > 100) + if (radios < 0 || radios > 100) return -EINVAL; if (channels < 1) @@ -2380,8 +2428,8 @@ static int __init init_mac80211_hwsim(void) } for (i = 0; i < radios; i++) { - err = mac80211_hwsim_create_radio(); - if (err) + err = mac80211_hwsim_create_radio(channels); + if (err < 0) goto out_free_radios; } diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h index afaad5a443b6..fb9920a99396 100644 --- a/drivers/net/wireless/mac80211_hwsim.h +++ b/drivers/net/wireless/mac80211_hwsim.h @@ -65,6 +65,9 @@ enum hwsim_tx_control_flags { * kernel, uses: * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS, * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE + * @HWSIM_CMD_CREATE_RADIO: create a new radio with the given parameters, + * returns the radio ID (>= 0) or negative on errors + * @HWSIM_CMD_DESTROY_RADIO: destroy a radio * @__HWSIM_CMD_MAX: enum limit */ enum { @@ -72,6 +75,8 @@ enum { HWSIM_CMD_REGISTER, HWSIM_CMD_FRAME, HWSIM_CMD_TX_INFO_FRAME, + HWSIM_CMD_CREATE_RADIO, + HWSIM_CMD_DESTROY_RADIO, __HWSIM_CMD_MAX, }; #define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1) @@ -94,6 +99,10 @@ enum { space * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame + * @HWSIM_ATTR_CHANNELS: u32 attribute used with the %HWSIM_CMD_CREATE_RADIO + * command giving the number of channels supported by the new radio + * @HWSIM_ATTR_RADIO_ID: u32 attribute used with %HWSIM_CMD_DESTROY_RADIO + * only to destroy a radio * @__HWSIM_ATTR_MAX: enum limit */ @@ -108,6 +117,8 @@ enum { HWSIM_ATTR_SIGNAL, HWSIM_ATTR_TX_INFO, HWSIM_ATTR_COOKIE, + HWSIM_ATTR_CHANNELS, + HWSIM_ATTR_RADIO_ID, __HWSIM_ATTR_MAX, }; #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) From 26b0e411d37a2ca5992d02884dc3fa4e1907e598 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jan 2014 13:37:38 +0100 Subject: [PATCH 068/121] mac80211_hwsim: restore regulatory testing functionality Restore the original regulatory testing functionality and also make it more flexible by allowing the parameters to be specified when creating a dynamic radio. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 218 +++++++++++++++++++++++++- drivers/net/wireless/mac80211_hwsim.h | 7 + 2 files changed, 222 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 056dae73b25c..dc7f72e3a4e7 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -57,6 +57,117 @@ static bool rctbl = false; module_param(rctbl, bool, 0444); MODULE_PARM_DESC(rctbl, "Handle rate control table"); +/** + * enum hwsim_regtest - the type of regulatory tests we offer + * + * These are the different values you can use for the regtest + * module parameter. This is useful to help test world roaming + * and the driver regulatory_hint() call and combinations of these. + * If you want to do specific alpha2 regulatory domain tests simply + * use the userspace regulatory request as that will be respected as + * well without the need of this module parameter. This is designed + * only for testing the driver regulatory request, world roaming + * and all possible combinations. + * + * @HWSIM_REGTEST_DISABLED: No regulatory tests are performed, + * this is the default value. + * @HWSIM_REGTEST_DRIVER_REG_FOLLOW: Used for testing the driver regulatory + * hint, only one driver regulatory hint will be sent as such the + * secondary radios are expected to follow. + * @HWSIM_REGTEST_DRIVER_REG_ALL: Used for testing the driver regulatory + * request with all radios reporting the same regulatory domain. + * @HWSIM_REGTEST_DIFF_COUNTRY: Used for testing the drivers calling + * different regulatory domains requests. Expected behaviour is for + * an intersection to occur but each device will still use their + * respective regulatory requested domains. Subsequent radios will + * use the resulting intersection. + * @HWSIM_REGTEST_WORLD_ROAM: Used for testing the world roaming. We accomplish + * this by using a custom beacon-capable regulatory domain for the first + * radio. All other device world roam. + * @HWSIM_REGTEST_CUSTOM_WORLD: Used for testing the custom world regulatory + * domain requests. All radios will adhere to this custom world regulatory + * domain. + * @HWSIM_REGTEST_CUSTOM_WORLD_2: Used for testing 2 custom world regulatory + * domain requests. The first radio will adhere to the first custom world + * regulatory domain, the second one to the second custom world regulatory + * domain. All other devices will world roam. + * @HWSIM_REGTEST_STRICT_FOLLOW_: Used for testing strict regulatory domain + * settings, only the first radio will send a regulatory domain request + * and use strict settings. The rest of the radios are expected to follow. + * @HWSIM_REGTEST_STRICT_ALL: Used for testing strict regulatory domain + * settings. All radios will adhere to this. + * @HWSIM_REGTEST_STRICT_AND_DRIVER_REG: Used for testing strict regulatory + * domain settings, combined with secondary driver regulatory domain + * settings. The first radio will get a strict regulatory domain setting + * using the first driver regulatory request and the second radio will use + * non-strict settings using the second driver regulatory request. All + * other devices should follow the intersection created between the + * first two. + * @HWSIM_REGTEST_ALL: Used for testing every possible mix. You will need + * at least 6 radios for a complete test. We will test in this order: + * 1 - driver custom world regulatory domain + * 2 - second custom world regulatory domain + * 3 - first driver regulatory domain request + * 4 - second driver regulatory domain request + * 5 - strict regulatory domain settings using the third driver regulatory + * domain request + * 6 and on - should follow the intersection of the 3rd, 4rth and 5th radio + * regulatory requests. + */ +enum hwsim_regtest { + HWSIM_REGTEST_DISABLED = 0, + HWSIM_REGTEST_DRIVER_REG_FOLLOW = 1, + HWSIM_REGTEST_DRIVER_REG_ALL = 2, + HWSIM_REGTEST_DIFF_COUNTRY = 3, + HWSIM_REGTEST_WORLD_ROAM = 4, + HWSIM_REGTEST_CUSTOM_WORLD = 5, + HWSIM_REGTEST_CUSTOM_WORLD_2 = 6, + HWSIM_REGTEST_STRICT_FOLLOW = 7, + HWSIM_REGTEST_STRICT_ALL = 8, + HWSIM_REGTEST_STRICT_AND_DRIVER_REG = 9, + HWSIM_REGTEST_ALL = 10, +}; + +/* Set to one of the HWSIM_REGTEST_* values above */ +static int regtest = HWSIM_REGTEST_DISABLED; +module_param(regtest, int, 0444); +MODULE_PARM_DESC(regtest, "The type of regulatory test we want to run"); + +static const char *hwsim_alpha2s[] = { + "FI", + "AL", + "US", + "DE", + "JP", + "AL", +}; + +static const struct ieee80211_regdomain hwsim_world_regdom_custom_01 = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), + REG_RULE(2484-10, 2484+10, 40, 0, 20, 0), + REG_RULE(5150-10, 5240+10, 40, 0, 30, 0), + REG_RULE(5745-10, 5825+10, 40, 0, 30, 0), + } +}; + +static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = { + .n_reg_rules = 2, + .alpha2 = "99", + .reg_rules = { + REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), + REG_RULE(5725-10, 5850+10, 40, 0, 30, + NL80211_RRF_NO_IR), + } +}; + +static const struct ieee80211_regdomain *hwsim_world_regdom_custom[] = { + &hwsim_world_regdom_custom_01, + &hwsim_world_regdom_custom_02, +}; + struct hwsim_vif_priv { u32 magic; u8 bssid[ETH_ALEN]; @@ -354,6 +465,9 @@ static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, [HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 }, [HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 }, + [HWSIM_ATTR_REG_HINT_ALPHA2] = { .type = NLA_STRING, .len = 2 }, + [HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 }, + [HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG }, }; static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, @@ -1820,7 +1934,9 @@ static const struct ieee80211_ops mac80211_hwsim_ops = { static struct ieee80211_ops mac80211_hwsim_mchan_ops; -static int mac80211_hwsim_create_radio(int channels) +static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2, + const struct ieee80211_regdomain *regd, + bool reg_strict) { int err; u8 addr[ETH_ALEN]; @@ -2000,6 +2116,15 @@ static int mac80211_hwsim_create_radio(int channels) hw->max_rates = 4; hw->max_rate_tries = 11; + if (reg_strict) + hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG; + if (regd) { + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(hw->wiphy, regd); + /* give the regulatory workqueue a chance to run */ + schedule_timeout_interruptible(1); + } + err = ieee80211_register_hw(hw); if (err < 0) { printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", @@ -2009,6 +2134,9 @@ static int mac80211_hwsim_create_radio(int channels) wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); + if (reg_alpha2) + regulatory_hint(hw->wiphy, reg_alpha2); + data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); debugfs_create_file("group", 0666, data->debugfs, data, @@ -2276,11 +2404,25 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2, static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info) { unsigned int chans = channels; + const char *alpha2 = NULL; + const struct ieee80211_regdomain *regd = NULL; + bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG]; if (info->attrs[HWSIM_ATTR_CHANNELS]) chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]); - return mac80211_hwsim_create_radio(chans); + if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]) + alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]); + + if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) { + u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]); + + if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) + return -EINVAL; + regd = hwsim_world_regdom_custom[idx]; + } + + return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict); } static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info) @@ -2428,7 +2570,77 @@ static int __init init_mac80211_hwsim(void) } for (i = 0; i < radios; i++) { - err = mac80211_hwsim_create_radio(channels); + const char *reg_alpha2 = NULL; + const struct ieee80211_regdomain *regd = NULL; + bool reg_strict = false; + + switch (regtest) { + case HWSIM_REGTEST_DIFF_COUNTRY: + if (i < ARRAY_SIZE(hwsim_alpha2s)) + reg_alpha2 = hwsim_alpha2s[i]; + break; + case HWSIM_REGTEST_DRIVER_REG_FOLLOW: + if (!i) + reg_alpha2 = hwsim_alpha2s[0]; + break; + case HWSIM_REGTEST_STRICT_ALL: + reg_strict = true; + case HWSIM_REGTEST_DRIVER_REG_ALL: + reg_alpha2 = hwsim_alpha2s[0]; + break; + case HWSIM_REGTEST_WORLD_ROAM: + if (i == 0) + regd = &hwsim_world_regdom_custom_01; + break; + case HWSIM_REGTEST_CUSTOM_WORLD: + regd = &hwsim_world_regdom_custom_01; + break; + case HWSIM_REGTEST_CUSTOM_WORLD_2: + if (i == 0) + regd = &hwsim_world_regdom_custom_01; + else if (i == 1) + regd = &hwsim_world_regdom_custom_02; + break; + case HWSIM_REGTEST_STRICT_FOLLOW: + if (i == 0) { + reg_strict = true; + reg_alpha2 = hwsim_alpha2s[0]; + } + break; + case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: + if (i == 0) { + reg_strict = true; + reg_alpha2 = hwsim_alpha2s[0]; + } else if (i == 1) { + reg_alpha2 = hwsim_alpha2s[1]; + } + break; + case HWSIM_REGTEST_ALL: + switch (i) { + case 0: + regd = &hwsim_world_regdom_custom_01; + break; + case 1: + regd = &hwsim_world_regdom_custom_02; + break; + case 2: + reg_alpha2 = hwsim_alpha2s[0]; + break; + case 3: + reg_alpha2 = hwsim_alpha2s[1]; + break; + case 4: + reg_strict = true; + reg_alpha2 = hwsim_alpha2s[2]; + break; + } + break; + default: + break; + } + + err = mac80211_hwsim_create_radio(channels, reg_alpha2, + regd, reg_strict); if (err < 0) goto out_free_radios; } diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h index fb9920a99396..2747cce5a269 100644 --- a/drivers/net/wireless/mac80211_hwsim.h +++ b/drivers/net/wireless/mac80211_hwsim.h @@ -103,6 +103,10 @@ enum { * command giving the number of channels supported by the new radio * @HWSIM_ATTR_RADIO_ID: u32 attribute used with %HWSIM_CMD_DESTROY_RADIO * only to destroy a radio + * @HWSIM_ATTR_REG_HINT_ALPHA2: alpha2 for regulatoro driver hint + * (nla string, length 2) + * @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute) + * @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute) * @__HWSIM_ATTR_MAX: enum limit */ @@ -119,6 +123,9 @@ enum { HWSIM_ATTR_COOKIE, HWSIM_ATTR_CHANNELS, HWSIM_ATTR_RADIO_ID, + HWSIM_ATTR_REG_HINT_ALPHA2, + HWSIM_ATTR_REG_CUSTOM_REG, + HWSIM_ATTR_REG_STRICT_REG, __HWSIM_ATTR_MAX, }; #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) From 5691e218a446753a2d21da231651b04e58c76b75 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 31 Dec 2013 21:05:50 +0200 Subject: [PATCH 069/121] iwlwifi: mvm: clear ap_ibss_active in case of failure The state variable was not set to false in case of a failure to complete the start_ap_ibss() flow. Signed-off-by: Ilan Peer Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 14a974e0ca2c..2ac3b25a6d2c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1012,6 +1012,9 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, if (ret) goto out_unbind; + /* must be set before quota calculations */ + mvmvif->ap_ibss_active = true; + ret = iwl_mvm_update_quotas(mvm, vif); if (ret) goto out_rm_bcast; @@ -1026,6 +1029,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, return 0; out_rm_bcast: + mvmvif->ap_ibss_active = false; iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); From a11e144e41f7979608c8bfd35b297b8162fcf32d Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 31 Dec 2013 21:19:55 +0200 Subject: [PATCH 070/121] iwlwifi: mvm: update power after binding in start_ap_ibss() The power settings need to be updated after a binding flow is done and before quota calculations. This was missing in the start_ap_ibss() flow. Fix it. Signed-off-by: Ilan Peer Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 2ac3b25a6d2c..208771a7c74c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1015,9 +1015,13 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* must be set before quota calculations */ mvmvif->ap_ibss_active = true; + /* power updated needs to be done before quotas */ + mvm->bound_vif_cnt++; + iwl_mvm_power_update_binding(mvm, vif, true); + ret = iwl_mvm_update_quotas(mvm, vif); if (ret) - goto out_rm_bcast; + goto out_quota_failed; /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) @@ -1028,7 +1032,9 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); return 0; -out_rm_bcast: +out_quota_failed: + mvm->bound_vif_cnt--; + iwl_mvm_power_update_binding(mvm, vif, false); mvmvif->ap_ibss_active = false; iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); out_unbind: @@ -1061,6 +1067,10 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); + + mvm->bound_vif_cnt--; + iwl_mvm_power_update_binding(mvm, vif, false); + iwl_mvm_mac_ctxt_remove(mvm, vif); mutex_unlock(&mvm->mutex); @@ -1772,11 +1782,11 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, } iwl_mvm_binding_remove_vif(mvm, vif); -out_unlock: - mvmvif->phy_ctxt = NULL; mvm->bound_vif_cnt--; iwl_mvm_power_update_binding(mvm, vif, false); +out_unlock: + mvmvif->phy_ctxt = NULL; mutex_unlock(&mvm->mutex); } From 91b0d1198417cf4fd9a7bd4138b6909f0b359099 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 5 Jan 2014 12:41:12 +0200 Subject: [PATCH 071/121] iwlwifi: mvm: fix missing cleanup in .start() error path Cleanup of iwl_mvm_leds was missing in case of error, resulting in the following warning: WARNING: at lib/kobject.c:196 kobject_add_internal+0x1f4/0x210() kobject_add_internal failed for phy0-led with -EEXIST, don't try to register things with the same name in the same directory. which prevents further reloads of the driver. Cc: stable@vger.kernel.org [3.10+] Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 552c76a926ed..2437a06bf60e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -472,6 +472,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, out_unregister: ieee80211_unregister_hw(mvm->hw); + iwl_mvm_leds_exit(mvm); out_free: iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); From 75d3e2d693a695d2a9df27de697fb73cb3f34c23 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 7 Jan 2014 18:19:35 +0200 Subject: [PATCH 072/121] iwlwifi: mvm: rs: fix handling of column switch error If we can't switch to a column because no rates are supported in that column this led to a state where the search cycle got stuck and never ended. This in turn also led to aggregation not being turned on. Fix this by marking a column as visited if we can't switch to it. Reported-and-tested-by: Karl Beldan Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/rs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index c9ff41c514ae..54887b952eb5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1591,6 +1591,8 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, search_tbl->column = col_id; rs_set_expected_tpt_table(lq_sta, search_tbl); + lq_sta->visited_columns |= BIT(col_id); + /* Get the best matching rate if we're changing modes. e.g. * SISO->MIMO, LEGACY->SISO, MIMO->SISO */ @@ -1614,7 +1616,6 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", col_id, rate->index); - lq_sta->visited_columns |= BIT(col_id); return 0; err: From 2775613f4ff32f8be2cae42d542b7fbefcafbfbe Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 2 Jan 2014 10:01:46 +0200 Subject: [PATCH 073/121] iwlwifi: mvm: don't set the drain bit when we flush the AP station When we disassociate in managed mode, we flush the queues after mac80211 has already removed the station. During that time, the pointer to ieee80211_sta to the fw_id_to_mac_id map is -EINVAL. In that case we should not set the station as being drained when the last Tx of this station has exited the shared Tx queue since we are flushing all the queues anyway. The draining logic is meant to be used in GO / AP mode only. In GO / AP mode, we set -EBUSY in the fw_id_to_mac_id map. This is why testing the ieee80211_sta pointer in the fw_id_to_mac_id map with IS_ERR isn't enough to set the station as draining, we need to check that it is -EBUSY. The only impact of the bug was a print: Drained sta 1, but it is internal? Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/tx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 3c575a39987b..a4d3bce48fbf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -704,7 +704,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, */ spin_lock_bh(&mvmsta->lock); sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (IS_ERR_OR_NULL(sta)) { + if (!sta || PTR_ERR(sta) == -EBUSY) { /* * Station disappeared in the meantime: * so we are draining. @@ -713,7 +713,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, schedule_work(&mvm->sta_drained_wk); } spin_unlock_bh(&mvmsta->lock); - } else if (!mvmsta) { + } else if (!mvmsta && PTR_ERR(sta) == -EBUSY) { /* Tx response without STA, so we are draining */ set_bit(sta_id, mvm->sta_drained); schedule_work(&mvm->sta_drained_wk); From 8fc685803a8d7b42a5f69dcad7f7cef6f8b60edf Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 14 Dec 2013 20:09:05 +0100 Subject: [PATCH 074/121] ath: fix warning on usage of REGULATORY_CUSTOM_REG ath wants to first apply the custom regd and only later will it revert to not using it if an alpha2 regulatory domain is found. Since the wireless core now enforces usage of the REGULATORY_CUSTOM_REG strictly when wiphy_apply_custom_regulatory() is used this makes ath adhere to the expected behaviour but also updates the wiphy after its done with the custom usage. This fixes this warning: [ 5.488733] ath: phy0: ASPM enabled: 0x43 [ 5.488735] ath: EEPROM regdomain: 0x0 [ 5.488736] ath: EEPROM indicates default country code should be used [ 5.488736] ath: doing EEPROM country->regdmn map search [ 5.488737] ath: country maps to regdmn code: 0x3a [ 5.488737] ath: Country alpha2 being used: US [ 5.488738] ath: Regpair used: 0x3a [ 5.488738] ------------[ cut here ]------------ [ 5.488745] WARNING: CPU: 0 PID: 161 at /home/sujith/dev/wireless-testing/net/wireless/reg.c:1361 wiphy_apply_custom_regulatory+0x17a/0x1b0 [cfg80211]() [ 5.488746] wiphy should have REGULATORY_CUSTOM_REG The wireless core can *later* lift this flag for us for when using the regulatory_hint() to make this fix more generic. Reported-by: Sujith Manoharan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/regd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 9e154732afaa..56c3d05c6224 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -632,7 +632,8 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, const struct ieee80211_regdomain *regd; wiphy->reg_notifier = reg_notifier; - wiphy->regulatory_flags |= REGULATORY_STRICT_REG; + wiphy->regulatory_flags |= REGULATORY_STRICT_REG | + REGULATORY_CUSTOM_REG; if (ath_is_world_regd(reg)) { /* @@ -640,8 +641,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, * saved on the wiphy orig_* parameters */ regd = ath_world_regdomain(reg); - wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | - REGULATORY_COUNTRY_IE_FOLLOW_POWER; + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_FOLLOW_POWER; } else { /* * This gets applied in the case of the absence of CRDA, @@ -650,7 +650,10 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, */ regd = ath_default_world_regdomain(); } + wiphy_apply_custom_regulatory(wiphy, regd); + if (!ath_is_world_regd(reg)) + wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG; ath_reg_apply_radar_flags(wiphy); ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg); return 0; From 4f7b91404cd5da3657a82b00394f4f5dfbff13d6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 14 Dec 2013 20:09:06 +0100 Subject: [PATCH 075/121] cfg80211: make regulatory_hint() remove REGULATORY_CUSTOM_REG The REGULATORY_CUSTOM_REG can be used during early init with the goal of overriding the wiphy's default regulatory settings in case the alpha2 of the device is not known. In the case that the alpha2 becomes known lets avoid having drivers having to clear the REGULATORY_CUSTOM_REG flag by doing it for them when regulatory_hint() is used. Cc: Sujith Manoharan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/regd.c | 2 -- include/net/regulatory.h | 4 ++++ net/wireless/reg.c | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 56c3d05c6224..e5e905910db4 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -652,8 +652,6 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, } wiphy_apply_custom_regulatory(wiphy, regd); - if (!ath_is_world_regd(reg)) - wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG; ath_reg_apply_radar_flags(wiphy); ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg); return 0; diff --git a/include/net/regulatory.h b/include/net/regulatory.h index c96a0b86f342..b07cdc9fa454 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -96,6 +96,10 @@ struct regulatory_request { * initiator is %REGDOM_SET_BY_CORE). Drivers that use * wiphy_apply_custom_regulatory() should have this flag set * or the regulatory core will set it for the wiphy. + * If you use regulatory_hint() *after* using + * wiphy_apply_custom_regulatory() the wireless core will + * clear the REGULATORY_CUSTOM_REG for your wiphy as it would be + * implied that the device somehow gained knowledge of its region. * @REGULATORY_STRICT_REG: tells us that the wiphy for this device * has regulatory domain that it wishes to be considered as the * superset for regulatory rules. After this device gets its regulatory diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7d20d844ca60..9b897fca7487 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1853,6 +1853,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) if (WARN_ON(!alpha2 || !wiphy)) return -EINVAL; + wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG; + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; From f87f960b2fb802f26ee3b00c19320e57a9c583ff Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 9 Jan 2014 10:27:27 -0600 Subject: [PATCH 076/121] rtlwifi: rtl8192cu: Add new device ID Reported-by: Jan Prinsloo Tested-by: Jan Prinsloo Signed-off-by: Larry Finger Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192cu/sw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index 8501954cfb44..c61311084d7e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -317,6 +317,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x0bda, 0x5088, rtl92cu_hal_cfg)}, /*Thinkware-CC&C*/ {RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ {RTL_USB_DEVICE(0x0df6, 0x005c, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ + {RTL_USB_DEVICE(0x0df6, 0x0077, rtl92cu_hal_cfg)}, /*Sitecom-WLA2100V2*/ {RTL_USB_DEVICE(0x0eb0, 0x9071, rtl92cu_hal_cfg)}, /*NO Brand - Etop*/ {RTL_USB_DEVICE(0x4856, 0x0091, rtl92cu_hal_cfg)}, /*NetweeN - Feixun*/ /* HP - Lite-On ,8188CUS Slim Combo */ From 68458dede7e0055198318dcb0318df3b4f419507 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 9 Jan 2014 19:36:55 -0800 Subject: [PATCH 077/121] mwifiex: add USB8897 support Adding new device IDs and assigning generic function/variable names instead of using device-id specific names. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: Bing Zhao Signed-off-by: Frank Huang Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/Kconfig | 4 +-- drivers/net/wireless/mwifiex/usb.c | 54 +++++++++++++++++++++------- drivers/net/wireless/mwifiex/usb.h | 12 ++++--- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig index f7ff4725506a..ecdf34505b54 100644 --- a/drivers/net/wireless/mwifiex/Kconfig +++ b/drivers/net/wireless/mwifiex/Kconfig @@ -31,12 +31,12 @@ config MWIFIEX_PCIE mwifiex_pcie. config MWIFIEX_USB - tristate "Marvell WiFi-Ex Driver for USB8797" + tristate "Marvell WiFi-Ex Driver for USB8797/8897" depends on MWIFIEX && USB select FW_LOADER ---help--- This adds support for wireless adapters based on Marvell - Avastar 88W8797 chipset with USB interface. + 8797/8897 chipset with USB interface. If you choose to build it as a module, it will be called mwifiex_usb. diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index a9240a2083f2..e8ebbd4bc3cd 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -22,15 +22,21 @@ #define USB_VERSION "1.0" -static const char usbdriver_name[] = "usb8797"; +static const char usbdriver_name[] = "usb8xxx"; static struct mwifiex_if_ops usb_ops; static struct semaphore add_remove_card_sem; static struct usb_card_rec *usb_card; static struct usb_device_id mwifiex_usb_table[] = { - {USB_DEVICE(USB8797_VID, USB8797_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8797_VID, USB8797_PID_2, + /* 8797 */ + {USB_DEVICE(USB8XXX_VID, USB8797_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + /* 8897 */ + {USB_DEVICE(USB8XXX_VID, USB8897_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8897_PID_2, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0xff)}, { } /* Terminating entry */ @@ -343,10 +349,20 @@ static int mwifiex_usb_probe(struct usb_interface *intf, id_vendor, id_product, bcd_device); /* PID_1 is used for firmware downloading only */ - if (id_product == USB8797_PID_1) - card->usb_boot_state = USB8797_FW_DNLD; - else - card->usb_boot_state = USB8797_FW_READY; + switch (id_product) { + case USB8797_PID_1: + case USB8897_PID_1: + card->usb_boot_state = USB8XXX_FW_DNLD; + break; + case USB8797_PID_2: + case USB8897_PID_2: + card->usb_boot_state = USB8XXX_FW_READY; + break; + default: + pr_warning("unknown id_product %#x\n", id_product); + card->usb_boot_state = USB8XXX_FW_DNLD; + break; + } card->udev = udev; card->intf = intf; @@ -755,9 +771,20 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) card->adapter = adapter; adapter->dev = &card->udev->dev; - strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); usb_card = card; + switch (le16_to_cpu(card->udev->descriptor.idProduct)) { + case USB8897_PID_1: + case USB8897_PID_2: + strcpy(adapter->fw_name, USB8897_DEFAULT_FW_NAME); + break; + case USB8797_PID_1: + case USB8797_PID_2: + default: + strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); + break; + } + return 0; } @@ -773,7 +800,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, { int ret = 0; u8 *firmware = fw->fw_buf, *recv_buff; - u32 retries = USB8797_FW_MAX_RETRY, dlen; + u32 retries = USB8XXX_FW_MAX_RETRY, dlen; u32 fw_seqnum = 0, tlen = 0, dnld_cmd = 0; struct fw_data *fwdata; struct fw_sync_header sync_fw; @@ -875,7 +902,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, continue; } - retries = USB8797_FW_MAX_RETRY; + retries = USB8XXX_FW_MAX_RETRY; break; } fw_seqnum++; @@ -899,13 +926,13 @@ static int mwifiex_usb_dnld_fw(struct mwifiex_adapter *adapter, int ret; struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - if (card->usb_boot_state == USB8797_FW_DNLD) { + if (card->usb_boot_state == USB8XXX_FW_DNLD) { ret = mwifiex_prog_fw_w_helper(adapter, fw); if (ret) return -1; /* Boot state changes after successful firmware download */ - if (card->usb_boot_state == USB8797_FW_DNLD) + if (card->usb_boot_state == USB8XXX_FW_DNLD) return -1; } @@ -1039,4 +1066,5 @@ MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell WiFi-Ex USB Driver version" USB_VERSION); MODULE_VERSION(USB_VERSION); MODULE_LICENSE("GPL v2"); -MODULE_FIRMWARE("mrvl/usb8797_uapsta.bin"); +MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8897_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h index 98c4316cd1a9..15b73d12e998 100644 --- a/drivers/net/wireless/mwifiex/usb.h +++ b/drivers/net/wireless/mwifiex/usb.h @@ -22,19 +22,23 @@ #include -#define USB8797_VID 0x1286 +#define USB8XXX_VID 0x1286 + #define USB8797_PID_1 0x2043 #define USB8797_PID_2 0x2044 +#define USB8897_PID_1 0x2045 +#define USB8897_PID_2 0x2046 -#define USB8797_FW_DNLD 1 -#define USB8797_FW_READY 2 -#define USB8797_FW_MAX_RETRY 3 +#define USB8XXX_FW_DNLD 1 +#define USB8XXX_FW_READY 2 +#define USB8XXX_FW_MAX_RETRY 3 #define MWIFIEX_TX_DATA_URB 6 #define MWIFIEX_RX_DATA_URB 6 #define MWIFIEX_USB_TIMEOUT 100 #define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin" +#define USB8897_DEFAULT_FW_NAME "mrvl/usb8897_uapsta.bin" #define FW_DNLD_TX_BUF_SIZE 620 #define FW_DNLD_RX_BUF_SIZE 2048 From b30d49b294fe339719918965594f12eb7967dca0 Mon Sep 17 00:00:00 2001 From: Alex Gal Date: Fri, 10 Jan 2014 15:21:13 -0500 Subject: [PATCH 078/121] wl12xx: fix tx power setting The driver ignores BSS_CHANGED_TXPOWER changes. Fix this by calling ACX_TX_POWER when appropriate. Signed-off-by: Alex Gal Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 18a009e593c7..b46b3116cc55 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4457,6 +4457,16 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out; + if ((changed & BSS_CHANGED_TXPOWER) && + bss_conf->txpower != wlvif->power_level) { + + ret = wl1271_acx_tx_power(wl, wlvif, bss_conf->txpower); + if (ret < 0) + goto out; + + wlvif->power_level = bss_conf->txpower; + } + if (is_ap) wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed); else From 9795229752c31da0c5f8a7dc4c827665327b52f9 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 10 Jan 2014 14:30:41 -0800 Subject: [PATCH 079/121] mwifiex: add missing endian conversion for fw_tsf It is u64 data received from firmware. Little endian to cpu conversion is required here. Cc: # 3.5+ Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 0ed06646f19a..0a8a26e10f01 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1681,7 +1681,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, const u8 *ie_buf; size_t ie_len; u16 channel = 0; - u64 fw_tsf = 0; + __le64 fw_tsf = 0; u16 beacon_size = 0; u32 curr_bcn_bytes; u32 freq; @@ -1815,7 +1815,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, ie_buf, ie_len, rssi, GFP_KERNEL); bss_priv = (struct mwifiex_bss_priv *)bss->priv; bss_priv->band = band; - bss_priv->fw_tsf = fw_tsf; + bss_priv->fw_tsf = le64_to_cpu(fw_tsf); if (priv->media_connected && !memcmp(bssid, priv->curr_bss_params.bss_descriptor From 09164043f63c947a49797750a09ca1cd7c31108e Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 12 Jan 2014 15:11:37 -0600 Subject: [PATCH 080/121] b43: Fix lockdep splat In https://bugzilla.kernel.org/show_bug.cgi?id=67561, a locking dependency is reported when b43 is used with hostapd, and rfkill is used to kill the radio output. The lockdep splat (in part) is as follows: ====================================================== [ INFO: possible circular locking dependency detected ] 3.12.0 #1 Not tainted ------------------------------------------------------- rfkill/10040 is trying to acquire lock: (rtnl_mutex){+.+.+.}, at: [] rtnl_lock+0x12/0x20 but task is already holding lock: (rfkill_global_mutex){+.+.+.}, at: [] rfkill_fop_write+0x6a/0x170 [rfkill] --snip-- Chain exists of: rtnl_mutex --> misc_mtx --> rfkill_global_mutex The fix is to move the initialization of the hardware random number generator outside the code range covered by the rtnl_mutex. Reported-by: yury Tested-by: yury Signed-off-by: Larry Finger Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ccd24f0acb8d..86b2030055d2 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2424,6 +2424,7 @@ error: static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl); static void b43_one_core_detach(struct b43_bus_dev *dev); +static int b43_rng_init(struct b43_wl *wl); static void b43_request_firmware(struct work_struct *work) { @@ -2475,6 +2476,10 @@ start_ieee80211: goto err_one_core_detach; wl->hw_registred = true; b43_leds_register(wl->current_dev); + + /* Register HW RNG driver */ + b43_rng_init(wl); + goto out; err_one_core_detach: @@ -4636,9 +4641,6 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) if (!dev || b43_status(dev) != B43_STAT_INITIALIZED) return; - /* Unregister HW RNG driver */ - b43_rng_exit(dev->wl); - b43_set_status(dev, B43_STAT_UNINIT); /* Stop the microcode PSM. */ @@ -4795,9 +4797,6 @@ static int b43_wireless_core_init(struct b43_wldev *dev) b43_set_status(dev, B43_STAT_INITIALIZED); - /* Register HW RNG driver */ - b43_rng_init(dev->wl); - out: return err; @@ -5464,6 +5463,9 @@ static void b43_bcma_remove(struct bcma_device *core) b43_one_core_detach(wldev->dev); + /* Unregister HW RNG driver */ + b43_rng_exit(wl); + b43_leds_unregister(wl); ieee80211_free_hw(wl->hw); @@ -5541,6 +5543,9 @@ static void b43_ssb_remove(struct ssb_device *sdev) b43_one_core_detach(dev); + /* Unregister HW RNG driver */ + b43_rng_exit(wl); + if (list_empty(&wl->devlist)) { b43_leds_unregister(wl); /* Last core on the chip unregistered. From 0673effd41dba323d6a280ef37b5ef29f3f5a653 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 12 Jan 2014 15:11:38 -0600 Subject: [PATCH 081/121] b43: Fix unload oops if firmware is not available The asyncronous firmware load uses a completion struct to hold firmware processing until the user-space routines are up and running. There is. however, a problem in that the waiter is nevered canceled during teardown. As a result, unloading the driver when firmware is not available causes an oops. To be able to access the completion structure at teardown, it had to be moved into the b43_wldev structure. This patch also fixes a typo in a comment. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/b43/b43.h | 4 ++-- drivers/net/wireless/b43/main.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 7f3d461f7e8d..54376fddfaf9 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -731,8 +731,6 @@ enum b43_firmware_file_type { struct b43_request_fw_context { /* The device we are requesting the fw for. */ struct b43_wldev *dev; - /* a completion event structure needed if this call is asynchronous */ - struct completion fw_load_complete; /* a pointer to the firmware object */ const struct firmware *blob; /* The type of firmware to request. */ @@ -809,6 +807,8 @@ enum { struct b43_wldev { struct b43_bus_dev *dev; struct b43_wl *wl; + /* a completion event structure needed if this call is asynchronous */ + struct completion fw_load_complete; /* The device initialization status. * Use b43_status() to query. */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 86b2030055d2..c75237eb55a1 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2070,6 +2070,7 @@ void b43_do_release_fw(struct b43_firmware_file *fw) static void b43_release_firmware(struct b43_wldev *dev) { + complete(&dev->fw_load_complete); b43_do_release_fw(&dev->fw.ucode); b43_do_release_fw(&dev->fw.pcm); b43_do_release_fw(&dev->fw.initvals); @@ -2095,7 +2096,7 @@ static void b43_fw_cb(const struct firmware *firmware, void *context) struct b43_request_fw_context *ctx = context; ctx->blob = firmware; - complete(&ctx->fw_load_complete); + complete(&ctx->dev->fw_load_complete); } int b43_do_request_fw(struct b43_request_fw_context *ctx, @@ -2142,7 +2143,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, } if (async) { /* do this part asynchronously */ - init_completion(&ctx->fw_load_complete); + init_completion(&ctx->dev->fw_load_complete); err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname, ctx->dev->dev->dev, GFP_KERNEL, ctx, b43_fw_cb); @@ -2150,12 +2151,11 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, pr_err("Unable to load firmware\n"); return err; } - /* stall here until fw ready */ - wait_for_completion(&ctx->fw_load_complete); + wait_for_completion(&ctx->dev->fw_load_complete); if (ctx->blob) goto fw_ready; /* On some ARM systems, the async request will fail, but the next sync - * request works. For this reason, we dall through here + * request works. For this reason, we fall through here */ } err = request_firmware(&ctx->blob, ctx->fwname, From 452028665312672c6ba9e16a19248ee00ead9400 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 12 Jan 2014 15:11:39 -0600 Subject: [PATCH 082/121] b43legacy: Fix unload oops if firmware is not available The asyncronous firmware load uses a completion struct to hold firmware processing until the user-space routines are up and running. There is. however, a problem in that the waiter is nevered canceled during teardown. As a result, unloading the driver when firmware is not available causes an oops. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/b43legacy/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 572668821862..349c77605231 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -3919,6 +3919,7 @@ static void b43legacy_remove(struct ssb_device *dev) * as the ieee80211 unreg will destroy the workqueue. */ cancel_work_sync(&wldev->restart_work); cancel_work_sync(&wl->firmware_load); + complete(&wldev->fw_load_complete); B43legacy_WARN_ON(!wl); if (!wldev->fw.ucode) From 1cdbaf0d3410b334255f638a2d69286ce2d775ec Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:29:27 +0530 Subject: [PATCH 083/121] ath9k: Add an option for station statistics Also, rename node_stat to node_aggr. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 8 ++ drivers/net/wireless/ath/ath9k/Makefile | 2 + drivers/net/wireless/ath/ath9k/debug.c | 80 ------------------ drivers/net/wireless/ath/ath9k/debug_sta.c | 97 ++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/main.c | 2 +- 5 files changed, 108 insertions(+), 81 deletions(-) create mode 100644 drivers/net/wireless/ath/ath9k/debug_sta.c diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 30d273c61bff..7b96b3e5712d 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -65,6 +65,14 @@ config ATH9K_DEBUGFS Also required for changing debug message flags at run time. +config ATH9K_STATION_STATISTICS + bool "Detailed station statistics" + depends on ATH9K && ATH9K_DEBUGFS && DEBUG_FS + select MAC80211_DEBUGFS + default n + ---help--- + This option enables detailed statistics for association stations. + config ATH9K_DFS_CERTIFIED bool "Atheros DFS support for certified platforms" depends on ATH9K && CFG80211_CERTIFICATION_ONUS diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index e9904e5ccd81..a40e5c5d7418 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -19,6 +19,8 @@ ath9k-$(CONFIG_ATH9K_WOW) += wow.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o \ spectral.o +ath9k-$(CONFIG_ATH9K_STATION_STATISTICS) += debug_sta.o + obj-$(CONFIG_ATH9K) += ath9k.o ath9k_hw-y:= \ diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index b041052a10ee..5bdc938ec5ed 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1322,86 +1322,6 @@ static const struct file_operations fops_btcoex = { }; #endif -static ssize_t read_file_node_stat(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_node *an = file->private_data; - struct ath_softc *sc = an->sc; - struct ath_atx_tid *tid; - struct ath_atx_ac *ac; - struct ath_txq *txq; - u32 len = 0, size = 4096; - char *buf; - size_t retval; - int tidno, acno; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - if (!an->sta->ht_cap.ht_supported) { - len = scnprintf(buf, size, "%s\n", - "HT not supported"); - goto exit; - } - - len = scnprintf(buf, size, "Max-AMPDU: %d\n", - an->maxampdu); - len += scnprintf(buf + len, size - len, "MPDU Density: %d\n\n", - an->mpdudensity); - - len += scnprintf(buf + len, size - len, - "%2s%7s\n", "AC", "SCHED"); - - for (acno = 0, ac = &an->ac[acno]; - acno < IEEE80211_NUM_ACS; acno++, ac++) { - txq = ac->txq; - ath_txq_lock(sc, txq); - len += scnprintf(buf + len, size - len, - "%2d%7d\n", - acno, ac->sched); - ath_txq_unlock(sc, txq); - } - - len += scnprintf(buf + len, size - len, - "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n", - "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", - "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); - - for (tidno = 0, tid = &an->tid[tidno]; - tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { - txq = tid->ac->txq; - ath_txq_lock(sc, txq); - len += scnprintf(buf + len, size - len, - "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n", - tid->tidno, tid->seq_start, tid->seq_next, - tid->baw_size, tid->baw_head, tid->baw_tail, - tid->bar_index, tid->sched, tid->paused); - ath_txq_unlock(sc, txq); - } -exit: - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - -static const struct file_operations fops_node_stat = { - .read = read_file_node_stat, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct dentry *dir) -{ - struct ath_node *an = (struct ath_node *)sta->drv_priv; - debugfs_create_file("node_stat", S_IRUGO, dir, an, &fops_node_stat); -} - /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c new file mode 100644 index 000000000000..3fe2bab2c4f5 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +static ssize_t read_file_node_aggr(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_node *an = file->private_data; + struct ath_softc *sc = an->sc; + struct ath_atx_tid *tid; + struct ath_atx_ac *ac; + struct ath_txq *txq; + u32 len = 0, size = 4096; + char *buf; + size_t retval; + int tidno, acno; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (!an->sta->ht_cap.ht_supported) { + len = scnprintf(buf, size, "%s\n", + "HT not supported"); + goto exit; + } + + len = scnprintf(buf, size, "Max-AMPDU: %d\n", + an->maxampdu); + len += scnprintf(buf + len, size - len, "MPDU Density: %d\n\n", + an->mpdudensity); + + len += scnprintf(buf + len, size - len, + "%2s%7s\n", "AC", "SCHED"); + + for (acno = 0, ac = &an->ac[acno]; + acno < IEEE80211_NUM_ACS; acno++, ac++) { + txq = ac->txq; + ath_txq_lock(sc, txq); + len += scnprintf(buf + len, size - len, + "%2d%7d\n", + acno, ac->sched); + ath_txq_unlock(sc, txq); + } + + len += scnprintf(buf + len, size - len, + "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n", + "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", + "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); + + for (tidno = 0, tid = &an->tid[tidno]; + tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { + txq = tid->ac->txq; + ath_txq_lock(sc, txq); + len += scnprintf(buf + len, size - len, + "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n", + tid->tidno, tid->seq_start, tid->seq_next, + tid->baw_size, tid->baw_head, tid->baw_tail, + tid->bar_index, tid->sched, tid->paused); + ath_txq_unlock(sc, txq); + } +exit: + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_node_aggr = { + .read = read_file_node_aggr, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct dentry *dir) +{ + struct ath_node *an = (struct ath_node *)sta->drv_priv; + debugfs_create_file("node_aggr", S_IRUGO, dir, an, &fops_node_aggr); +} diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d0c3aec7c74e..07fcf1609726 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2111,7 +2111,7 @@ struct ieee80211_ops ath9k_ops = { .get_et_strings = ath9k_get_et_strings, #endif -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_STATION_STATISTICS) .sta_add_debugfs = ath9k_sta_add_debugfs, #endif .sw_scan_start = ath9k_sw_scan_start, From 78175fc45da3066337fcacbff562097bfda2ce7e Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:29:28 +0530 Subject: [PATCH 084/121] ath9k: Show only active TIDs in node_aggr Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug_sta.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index 3fe2bab2c4f5..3d119689d08a 100644 --- a/drivers/net/wireless/ath/ath9k/debug_sta.c +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -66,11 +66,19 @@ static ssize_t read_file_node_aggr(struct file *file, char __user *user_buf, tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { txq = tid->ac->txq; ath_txq_lock(sc, txq); - len += scnprintf(buf + len, size - len, - "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n", - tid->tidno, tid->seq_start, tid->seq_next, - tid->baw_size, tid->baw_head, tid->baw_tail, - tid->bar_index, tid->sched, tid->paused); + if (tid->active) { + len += scnprintf(buf + len, size - len, + "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n", + tid->tidno, + tid->seq_start, + tid->seq_next, + tid->baw_size, + tid->baw_head, + tid->baw_tail, + tid->bar_index, + tid->sched, + tid->paused); + } ath_txq_unlock(sc, txq); } exit: From c3b9f9e86b178fd3e94e2d6dd6da84ab38fa2ba7 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:29:29 +0530 Subject: [PATCH 085/121] ath9k: Use a separate debugfs file for PHY errors Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 117 ++++++++++++++++--------- 1 file changed, 74 insertions(+), 43 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 5bdc938ec5ed..ab7264c1d8f7 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -943,14 +943,10 @@ static const struct file_operations fops_reset = { static ssize_t read_file_recv(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { -#define PHY_ERR(s, p) \ - len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \ - sc->debug.stats.rxstats.phy_err_stats[p]); - #define RXS_ERR(s, e) \ do { \ len += scnprintf(buf + len, size - len, \ - "%22s : %10u\n", s, \ + "%18s : %10u\n", s, \ sc->debug.stats.rxstats.e);\ } while (0) @@ -963,6 +959,12 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; + RXS_ERR("PKTS-ALL", rx_pkts_all); + RXS_ERR("BYTES-ALL", rx_bytes_all); + RXS_ERR("BEACONS", rx_beacons); + RXS_ERR("FRAGS", rx_frags); + RXS_ERR("SPECTRAL", rx_spectral); + RXS_ERR("CRC ERR", crc_err); RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err); RXS_ERR("PHY ERR", phy_err); @@ -970,43 +972,10 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err); RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err); RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err); - RXS_ERR("RX-LENGTH-ERR", rx_len_err); - RXS_ERR("RX-OOM-ERR", rx_oom_err); - RXS_ERR("RX-RATE-ERR", rx_rate_err); - RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err); - - PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); - PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); - PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY); - PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE); - PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH); - PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); - PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); - PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); - PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); - PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); - PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); - PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); - PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); - PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); - PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); - PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); - PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); - PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); - PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); - PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); - PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); - PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); - PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); - PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); - PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); - PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); - - RXS_ERR("RX-Pkts-All", rx_pkts_all); - RXS_ERR("RX-Bytes-All", rx_bytes_all); - RXS_ERR("RX-Beacons", rx_beacons); - RXS_ERR("RX-Frags", rx_frags); - RXS_ERR("RX-Spectral", rx_spectral); + RXS_ERR("LENGTH-ERR", rx_len_err); + RXS_ERR("OOM-ERR", rx_oom_err); + RXS_ERR("RATE-ERR", rx_rate_err); + RXS_ERR("TOO-MANY-FRAGS", rx_too_many_frags_err); if (len > size) len = size; @@ -1017,7 +986,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, return retval; #undef RXS_ERR -#undef PHY_ERR } void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) @@ -1056,6 +1024,67 @@ static const struct file_operations fops_recv = { .llseek = default_llseek, }; +static ssize_t read_file_phy_err(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define PHY_ERR(s, p) \ + len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \ + sc->debug.stats.rxstats.phy_err_stats[p]); + + struct ath_softc *sc = file->private_data; + char *buf; + unsigned int len = 0, size = 1600; + ssize_t retval = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); + PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); + PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY); + PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE); + PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH); + PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); + PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); + PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); + PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); + PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); + PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); + PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); + PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); + PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); + PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); + PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); + PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); + PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); + PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); + PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); + PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); + PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); + PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); + PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); + PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); + PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; + +#undef PHY_ERR +} + +static const struct file_operations fops_phy_err = { + .read = read_file_phy_err, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static ssize_t read_file_regidx(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1489,6 +1518,8 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_reset); debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_recv); + debugfs_create_file("phy_err", S_IRUSR, sc->debug.debugfs_phy, sc, + &fops_phy_err); debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy, &ah->rxchainmask); debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy, From 350e2dcb4647d144000db1505c94d586fcd57919 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:29:30 +0530 Subject: [PATCH 086/121] ath9k: Add a debugfs file "node_recv" This would be useful when debugging RX performance issues. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 8 +- drivers/net/wireless/ath/ath9k/debug.h | 34 ++++- drivers/net/wireless/ath/ath9k/debug_sta.c | 164 +++++++++++++++++++++ drivers/net/wireless/ath/ath9k/recv.c | 2 +- drivers/net/wireless/ath/ath9k/xmit.c | 2 - 5 files changed, 203 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index f622a986c8cc..cba7d772f7f3 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -146,7 +146,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_AN_2_TID(_an, _tidno) (&(_an)->tid[(_tidno)]) -#define IS_CCK_RATE(rate) ((rate >= 0x18) && (rate <= 0x1e)) +#define IS_HT_RATE(rate) (rate & 0x80) +#define IS_CCK_RATE(rate) ((rate >= 0x18) && (rate <= 0x1e)) +#define IS_OFDM_RATE(rate) ((rate >= 0x8) && (rate <= 0xf)) struct ath_txq { int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */ @@ -262,6 +264,10 @@ struct ath_node { bool sleeping; bool no_ps_filter; + +#ifdef CONFIG_ATH9K_STATION_STATISTICS + struct ath_rx_rate_stats rx_rate_stats; +#endif }; struct ath_tx_control { diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index ec02d38ea8ea..b01b8c3ec570 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -27,11 +27,13 @@ struct fft_sample_tlv; #ifdef CONFIG_ATH9K_DEBUGFS #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++ +#define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) #define RESET_STAT_INC(sc, type) sc->debug.stats.reset[type]++ #define ANT_STAT_INC(i, c) sc->debug.stats.ant_stats[i].c++ #define ANT_LNA_INC(i, c) sc->debug.stats.ant_stats[i].lna_recv_cnt[c]++; #else #define TX_STAT_INC(q, c) do { } while (0) +#define RX_STAT_INC(c) #define RESET_STAT_INC(sc, type) do { } while (0) #define ANT_STAT_INC(i, c) do { } while (0) #define ANT_LNA_INC(i, c) do { } while (0) @@ -201,7 +203,23 @@ struct ath_tx_stats { TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \ } while(0) -#define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) +struct ath_rx_rate_stats { + struct { + u32 ht20_cnt; + u32 ht40_cnt; + u32 sgi_cnt; + u32 lgi_cnt; + } ht_stats[24]; + + struct { + u32 ofdm_cnt; + } ofdm_stats[8]; + + struct { + u32 cck_lp_cnt; + u32 cck_sp_cnt; + } cck_stats[4]; +}; /** * struct ath_rx_stats - RX Statistics @@ -299,8 +317,6 @@ void ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause); #else -#define RX_STAT_INC(c) /* NOP */ - static inline int ath9k_init_debug(struct ath_hw *ah) { return 0; @@ -338,4 +354,16 @@ ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause) #endif /* CONFIG_ATH9K_DEBUGFS */ +#ifdef CONFIG_ATH9K_STATION_STATISTICS +void ath_debug_rate_stats(struct ath_softc *sc, + struct ath_rx_status *rs, + struct sk_buff *skb); +#else +static inline void ath_debug_rate_stats(struct ath_softc *sc, + struct ath_rx_status *rs, + struct sk_buff *skb) +{ +} +#endif /* CONFIG_ATH9K_STATION_STATISTICS */ + #endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index 3d119689d08a..d33cc88a5849 100644 --- a/drivers/net/wireless/ath/ath9k/debug_sta.c +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -16,6 +16,10 @@ #include "ath9k.h" +/*************/ +/* node_aggr */ +/*************/ + static ssize_t read_file_node_aggr(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -95,11 +99,171 @@ static const struct file_operations fops_node_aggr = { .llseek = default_llseek, }; +/*************/ +/* node_recv */ +/*************/ + +void ath_debug_rate_stats(struct ath_softc *sc, + struct ath_rx_status *rs, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_rx_status *rxs; + struct ath_rx_rate_stats *rstats; + struct ieee80211_sta *sta; + struct ath_node *an; + + if (!ieee80211_is_data(hdr->frame_control)) + return; + + rcu_read_lock(); + + sta = ieee80211_find_sta_by_ifaddr(sc->hw, hdr->addr2, NULL); + if (!sta) + goto exit; + + an = (struct ath_node *) sta->drv_priv; + rstats = &an->rx_rate_stats; + rxs = IEEE80211_SKB_RXCB(skb); + + if (IS_HT_RATE(rs->rs_rate)) { + if (rxs->rate_idx > ARRAY_SIZE(rstats->ht_stats)) + goto exit; + + if (rxs->flag & RX_FLAG_40MHZ) + rstats->ht_stats[rxs->rate_idx].ht40_cnt++; + else + rstats->ht_stats[rxs->rate_idx].ht20_cnt++; + + if (rxs->flag & RX_FLAG_SHORT_GI) + rstats->ht_stats[rxs->rate_idx].sgi_cnt++; + else + rstats->ht_stats[rxs->rate_idx].lgi_cnt++; + + goto exit; + } + + if (IS_CCK_RATE(rs->rs_rate)) { + if (rxs->flag & RX_FLAG_SHORTPRE) + rstats->cck_stats[rxs->rate_idx].cck_sp_cnt++; + else + rstats->cck_stats[rxs->rate_idx].cck_lp_cnt++; + + goto exit; + } + + if (IS_OFDM_RATE(rs->rs_rate)) { + if (ah->curchan->chan->band == IEEE80211_BAND_2GHZ) + rstats->ofdm_stats[rxs->rate_idx - 4].ofdm_cnt++; + else + rstats->ofdm_stats[rxs->rate_idx].ofdm_cnt++; + } +exit: + rcu_read_unlock(); +} + +#define PRINT_CCK_RATE(str, i, sp) \ + do { \ + len += scnprintf(buf + len, size - len, \ + "%11s : %10u\n", \ + str, \ + (sp) ? rstats->cck_stats[i].cck_sp_cnt : \ + rstats->cck_stats[i].cck_lp_cnt); \ + } while (0) + +#define PRINT_OFDM_RATE(str, i) \ + do { \ + len += scnprintf(buf + len, size - len, \ + "%11s : %10u\n", \ + str, \ + rstats->ofdm_stats[i].ofdm_cnt); \ + } while (0) + +static ssize_t read_file_node_recv(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_node *an = file->private_data; + struct ath_softc *sc = an->sc; + struct ath_hw *ah = sc->sc_ah; + struct ath_rx_rate_stats *rstats; + struct ieee80211_sta *sta = an->sta; + enum ieee80211_band band; + u32 len = 0, size = 4096; + char *buf; + size_t retval; + int i; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + band = ah->curchan->chan->band; + rstats = &an->rx_rate_stats; + + if (!sta->ht_cap.ht_supported) + goto legacy; + + len += scnprintf(buf + len, size - len, + "%24s%10s%10s%10s\n", + "HT20", "HT40", "SGI", "LGI"); + + for (i = 0; i < 24; i++) { + len += scnprintf(buf + len, size - len, + "%8s%3u : %10u%10u%10u%10u\n", + "MCS", i, + rstats->ht_stats[i].ht20_cnt, + rstats->ht_stats[i].ht40_cnt, + rstats->ht_stats[i].sgi_cnt, + rstats->ht_stats[i].lgi_cnt); + } + + len += scnprintf(buf + len, size - len, "\n"); + +legacy: + if (band == IEEE80211_BAND_2GHZ) { + PRINT_CCK_RATE("CCK-1M/LP", 0, false); + PRINT_CCK_RATE("CCK-2M/LP", 1, false); + PRINT_CCK_RATE("CCK-5.5M/LP", 2, false); + PRINT_CCK_RATE("CCK-11M/LP", 3, false); + + PRINT_CCK_RATE("CCK-2M/SP", 1, true); + PRINT_CCK_RATE("CCK-5.5M/SP", 2, true); + PRINT_CCK_RATE("CCK-11M/SP", 3, true); + } + + PRINT_OFDM_RATE("OFDM-6M", 0); + PRINT_OFDM_RATE("OFDM-9M", 1); + PRINT_OFDM_RATE("OFDM-12M", 2); + PRINT_OFDM_RATE("OFDM-18M", 3); + PRINT_OFDM_RATE("OFDM-24M", 4); + PRINT_OFDM_RATE("OFDM-36M", 5); + PRINT_OFDM_RATE("OFDM-48M", 6); + PRINT_OFDM_RATE("OFDM-54M", 7); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +#undef PRINT_OFDM_RATE +#undef PRINT_CCK_RATE + +static const struct file_operations fops_node_recv = { + .read = read_file_node_recv, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir) { struct ath_node *an = (struct ath_node *)sta->drv_priv; + debugfs_create_file("node_aggr", S_IRUGO, dir, an, &fops_node_aggr); + debugfs_create_file("node_recv", S_IRUGO, dir, an, &fops_node_recv); } diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index f7cc5b37a18f..5bf3243dce0f 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1354,8 +1354,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_antenna_check(sc, &rs); - ath9k_apply_ampdu_details(sc, &rs, rxs); + ath_debug_rate_stats(sc, &rs, skb); ieee80211_rx(hw, skb); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index e8d0e7fc77da..0a75e2f68c9d 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -47,8 +47,6 @@ static u16 bits_per_symbol[][2] = { { 260, 540 }, /* 7: 64-QAM 5/6 */ }; -#define IS_HT_RATE(_rate) ((_rate) & 0x80) - static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct sk_buff *skb); static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, From 9d89cadd39f78eb4dd70d522dc3fd17b3c41de58 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:40:56 +0530 Subject: [PATCH 087/121] ath9k: Remove debug print in ISR There is no need to do this and we can avoid an unused variable warning when CONFIG_ATH9K_WOW is not selected. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 07fcf1609726..ca8aff1aec94 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -542,7 +542,6 @@ irqreturn_t ath_isr(int irq, void *dev) struct ath_softc *sc = dev; struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); enum ath9k_int status; u32 sync_cause = 0; bool sched = false; @@ -603,7 +602,6 @@ irqreturn_t ath_isr(int irq, void *dev) #ifdef CONFIG_ATH9K_WOW if (status & ATH9K_INT_BMISS) { if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { - ath_dbg(common, ANY, "during WoW we got a BMISS\n"); atomic_inc(&sc->wow_got_bmiss_intr); atomic_dec(&sc->wow_sleep_proc_intr); } From 071aa9a8b2e724679ac2cf14f31b781a3783c377 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 13:55:11 +0530 Subject: [PATCH 088/121] ath9k: Process GTT interrupts Global Transmission Timeout interrupts are generated by the HW when transmission of a frame fails - this is done based on the threshold programmed in the AR_GTXTO register. Currently, even though the interrupt is enabled for all chips, it is not handled in the driver. This patch handles GTT events for AR9003 and above chips, checking if the MAC/BB has hung after successive GTT interrupts crosses a threshold (5). This can be enabled for the older chips in the AR9002 family once appropriate HW hang checks are implemented for them. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 ++ drivers/net/wireless/ath/ath9k/debug.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 36 +++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index cba7d772f7f3..b5ac32cfbeb8 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -691,6 +691,7 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); #define DEFAULT_CACHELINE 32 #define ATH_CABQ_READY_TIME 80 /* % of beacon interval */ #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ +#define MAX_GTT_CNT 5 enum sc_op_flags { SC_OP_INVALID, @@ -733,6 +734,7 @@ struct ath_softc { unsigned long sc_flags; unsigned long driver_data; + u8 gtt_cnt; u32 intrstatus; u16 ps_flags; /* PS_* */ u16 curtxpow; diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index b01b8c3ec570..cc7a025d833e 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -44,6 +44,7 @@ enum ath_reset_type { RESET_TYPE_BB_WATCHDOG, RESET_TYPE_FATAL_INT, RESET_TYPE_TX_ERROR, + RESET_TYPE_TX_GTT, RESET_TYPE_TX_HANG, RESET_TYPE_PLL_HANG, RESET_TYPE_MAC_HANG, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index ca8aff1aec94..73a36551a5ed 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -258,6 +258,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) } } + sc->gtt_cnt = 0; ieee80211_wake_queues(sc->hw); return true; @@ -476,6 +477,19 @@ void ath9k_tasklet(unsigned long data) } } + if (status & ATH9K_INT_GTT) { + sc->gtt_cnt++; + + if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) { + type = RESET_TYPE_TX_GTT; + ath9k_queue_reset(sc, type); + atomic_inc(&ah->intr_ref_cnt); + ath_dbg(common, ANY, + "GTT: Skipping interrupts\n"); + goto out; + } + } + spin_lock_irqsave(&sc->sc_pm_lock, flags); if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) { /* @@ -503,10 +517,19 @@ void ath9k_tasklet(unsigned long data) } if (status & ATH9K_INT_TX) { - if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) + if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { + /* + * For EDMA chips, TX completion is enabled for the + * beacon queue, so if a beacon has been transmitted + * successfully after a GTT interrupt, the GTT counter + * gets reset to zero here. + */ + /* sc->gtt_cnt = 0; */ + ath_tx_edma_tasklet(sc); - else + } else { ath_tx_tasklet(sc); + } wake_up(&sc->tx_wait); } @@ -536,6 +559,7 @@ irqreturn_t ath_isr(int irq, void *dev) ATH9K_INT_TX | \ ATH9K_INT_BMISS | \ ATH9K_INT_CST | \ + ATH9K_INT_GTT | \ ATH9K_INT_TSFOOR | \ ATH9K_INT_GENTIMER | \ ATH9K_INT_MCI) @@ -608,7 +632,6 @@ irqreturn_t ath_isr(int irq, void *dev) } #endif - if (status & ATH9K_INT_SWBA) tasklet_schedule(&sc->bcon_tasklet); @@ -733,7 +756,12 @@ static int ath9k_start(struct ieee80211_hw *hw) if (ah->config.hw_hang_checks & HW_BB_WATCHDOG) ah->imask |= ATH9K_INT_BB_WATCHDOG; - ah->imask |= ATH9K_INT_GTT; + /* + * Enable GTT interrupts only for AR9003/AR9004 chips + * for now. + */ + if (AR_SREV_9300_20_OR_LATER(ah)) + ah->imask |= ATH9K_INT_GTT; if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) ah->imask |= ATH9K_INT_CST; From 6dc75f1c9ff912cdfea4cf6a2bb5957ea174a9e5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 13 Jan 2014 17:17:26 +0100 Subject: [PATCH 089/121] brcmsmac: delete useless variable Delete a variable that is at most only assigned to a constant, but never used otherwise. A simplified version of the semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ type T; identifier i; constant c; @@ -T i; <... when != i -i = c; ...> // Signed-off-by: Julia Lawall Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 8138f1cff4e5..9417cb5a2553 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -7108,7 +7108,6 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, struct sk_buff *p, struct ieee80211_rx_status *rx_status) { - int preamble; int channel; u32 rspec; unsigned char *plcp; @@ -7191,7 +7190,6 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, rx_status->rate_idx -= BRCMS_LEGACY_5G_RATE_OFFSET; /* Determine short preamble and rate_idx */ - preamble = 0; if (is_cck_rate(rspec)) { if (rxh->PhyRxStatus_0 & PRXS0_SHORTH) rx_status->flag |= RX_FLAG_SHORTPRE; From 608cfbe4abaf76e9d732efd7ed1cfa3998163d91 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 13 Jan 2014 22:05:23 +0300 Subject: [PATCH 090/121] p54: clamp properly instead of just truncating The call to clamp_t() first truncates the variable signed 8 bit and as a result, the actual clamp is a no-op. Fixes: 0d78156eef1d ('p54: improve site survey') Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 9c96831c0b5c..153c61539ec8 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -586,7 +586,7 @@ static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb) chan = priv->curchan; if (chan) { struct survey_info *survey = &priv->survey[chan->hw_value]; - survey->noise = clamp_t(s8, priv->noise, -128, 127); + survey->noise = clamp(priv->noise, -128, 127); survey->channel_time = priv->survey_raw.active; survey->channel_time_tx = priv->survey_raw.tx; survey->channel_time_busy = priv->survey_raw.tx + From 2d93aee152b1758a94a18fe15d72153ba73b5679 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Dec 2013 14:15:41 +0200 Subject: [PATCH 091/121] iwlwifi: pcie: enable oscillator for L1 exit Enabling the oscillator consumes slightly more power (100uA) but allows to make sure that we exit from L1 on time. Not doing so might lead to a PCIe specification violation since we might wake up from L1 at the wrong time. This issue has been identified on 3160 and 7260 only. On older NICs L1 off is not enabled, on newer NICs (7265), the issue is fixed. When the bug occurs the user sees that the NIC has disappeared from the PCI bridge, any access to the device returns 0xff. This fixes: https://bugzilla.kernel.org/show_bug.cgi?id=64541 and has been extensively discussed here: http://markmail.org/thread/mfmpzqt3r333n4bo Cc: stable@vger.kernel.org [3.10+] Fixes: 99cd47142399 ("iwlwifi: add 7000 series device configuration") Reported-and-tested-by: wzyboy Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-prph.h | 4 ++++ drivers/net/wireless/iwlwifi/pcie/trans.c | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index d69b0fb0a434..100bd0d79681 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -277,4 +277,8 @@ static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) /*********************** END TX SCHEDULER *************************************/ +/* Oscillator clock */ +#define OSC_CLK (0xa04068) +#define OSC_CLK_FORCE_CONTROL (0x8) + #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 16f66c1a23de..f9507807b486 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -178,6 +178,28 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) goto out; } + if (trans->cfg->host_interrupt_operation_mode) { + /* + * This is a bit of an abuse - This is needed for 7260 / 3160 + * only check host_interrupt_operation_mode even if this is + * not related to host_interrupt_operation_mode. + * + * Enable the oscillator to count wake up time for L1 exit. This + * consumes slightly more power (100uA) - but allows to be sure + * that we wake up from L1 on time. + * + * This looks weird: read twice the same register, discard the + * value, set a bit, and yet again, read that same register + * just to discard the value. But that's the way the hardware + * seems to like it. + */ + iwl_read_prph(trans, OSC_CLK); + iwl_read_prph(trans, OSC_CLK); + iwl_set_bits_prph(trans, OSC_CLK, OSC_CLK_FORCE_CONTROL); + iwl_read_prph(trans, OSC_CLK); + iwl_read_prph(trans, OSC_CLK); + } + /* * Enable DMA clock and wait for it to stabilize. * From cf9d11888091920e356d085a5fa1644fc5c05429 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Dec 2013 18:54:06 +0200 Subject: [PATCH 092/121] iwlwifi: mvm: fix tx seq_ctrl debug print Since seq_number is incremented right after using it, so printed seq_ctrl was actually the next one to be used. Fix it by incrementing the seq_number only later, before saving it. Additionally, use the IEEE80211_SEQ_TO_SN macro in order to print the actual sequence number. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/tx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index a4d3bce48fbf..90378c217bc7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -390,7 +390,6 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seq_number); - seq_number += 0x10; is_data_qos = true; is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; } @@ -407,13 +406,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, } IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, - tid, txq_id, seq_number); + tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number)); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) goto drop_unlock_sta; if (is_data_qos && !ieee80211_has_morefrags(fc)) - mvmsta->tid_data[tid].seq_number = seq_number; + mvmsta->tid_data[tid].seq_number = seq_number + 0x10; spin_unlock(&mvmsta->lock); From 099d8f20b65d0dac07fb6e2ca0ed19e5d36d7d2e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 5 Jan 2014 15:09:44 +0200 Subject: [PATCH 093/121] iwlwifi: mvm: send all the NVM sections to the NIC Some NIC comes with more than the 4 NVM (non volative memory) sections described in the nvm_to_read array. These NICs usually get their NVM from an external file fetched from userland during init. We already parsed the file, but sent to the NIC only 4 NVM sections whereas there could be more sections in the file. Fix this. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index c6beb0f842d5..9affd43db811 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -393,16 +393,15 @@ out: int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) { int i, ret; - u16 section_id; struct iwl_nvm_section *sections = mvm->nvm_sections; IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); - for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { - section_id = nvm_to_read[i]; - ret = iwl_nvm_write_section(mvm, section_id, - sections[section_id].data, - sections[section_id].length); + for (i = 0; i < ARRAY_SIZE(mvm->nvm_sections); i++) { + if (!mvm->nvm_sections[i].data || !mvm->nvm_sections[i].length) + continue; + ret = iwl_nvm_write_section(mvm, i, sections[i].data, + sections[i].length); if (ret < 0) { IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); break; From 560843f4ab7ba8c2e5edf01949bba4d7a94f01e9 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sun, 5 Jan 2014 21:04:19 +0200 Subject: [PATCH 094/121] iwlwifi: mvm: rs: fix a theoretical out of bounds access Discovered by klocwork Array 'iwl_rate_mcs' of size 15 may use index value(s) -1 * rs.c:2562: index = iwl_hwrate_to_plcp_idx(rate) * rs.c:2562: Result of function call 'iwl_hwrate_to_plcp_idx(rate)' is '[-1,14]' * rs.c:2565: Array 'iwl_rate_mcs' size is 15. * rs.c:2565: Possible attempt to access element -1 of array 'iwl_rate_mcs'. While at it stop using index = -1 and always use IWL_RATE_INVALID Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/rs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 54887b952eb5..c4f214d4c44e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -357,7 +357,7 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) return idx; } - return -1; + return IWL_RATE_INVALID; } static void rs_rate_scale_perform(struct iwl_mvm *mvm, @@ -703,10 +703,8 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, memset(rate, 0, sizeof(*rate)); rate->index = iwl_hwrate_to_plcp_idx(ucode_rate); - if (rate->index == IWL_RATE_INVALID) { - rate->index = -1; + if (rate->index == IWL_RATE_INVALID) return -EINVAL; - } rate->ant = (ant_msk >> RATE_MCS_ANT_POS); @@ -2562,7 +2560,9 @@ static int rs_pretty_print_rate(char *buf, const u32 rate) int index = iwl_hwrate_to_plcp_idx(rate); return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n", - rs_pretty_ant(ant), iwl_rate_mcs[index].mbps); + rs_pretty_ant(ant), + index == IWL_RATE_INVALID ? "BAD" : + iwl_rate_mcs[index].mbps); } if (rate & RATE_MCS_VHT_MSK) { From 01e0efe3178dbeed1284e55b70a3160a279995b2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 6 Jan 2014 09:05:54 +0200 Subject: [PATCH 095/121] iwlwifi: mvm: fix SRAM dump debugfs handler If the length isn't set it means we want all the SRAM. Also - this is perfectly valid to partially dump starting at offset 0. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 76cdce9edf55..369d4c90e669 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -135,7 +135,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; len = img->sec[IWL_UCODE_SECTION_DATA].len; - if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) { + if (mvm->dbgfs_sram_len) { ofs = mvm->dbgfs_sram_offset; len = mvm->dbgfs_sram_len; } From 9ddca8607fc1850376a018b3be0a437f0bf99a2f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 09:29:40 +0100 Subject: [PATCH 096/121] iwlwifi: mvm: use array indexing instead of treating it as a pointer It's a bit strange to treat an array as a pointer, so use proper array indexing instead. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 208771a7c74c..797b4d91cfd7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -262,9 +262,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; /* currently FW API supports only one optional cipher scheme */ - if (mvm->fw->cs->cipher) { + if (mvm->fw->cs[0].cipher) { mvm->hw->n_cipher_schemes = 1; - mvm->hw->cipher_schemes = mvm->fw->cs; + mvm->hw->cipher_schemes = &mvm->fw->cs[0]; } #ifdef CONFIG_PM_SLEEP From 4af8beaa7b32ca3e7479bf0189f973ac40c1cff6 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 1 Jan 2014 10:05:55 +0200 Subject: [PATCH 097/121] iwlwifi: mvm: remove unused flags from add station command Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 6bbbad453a3b..1b60fdff6a56 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -97,9 +97,6 @@ enum iwl_sta_flags { STA_FLG_FLG_ANT_B), STA_FLG_PS = BIT(8), - STA_FLG_INVALID = BIT(9), - STA_FLG_DLP_EN = BIT(10), - STA_FLG_SET_ALL_KEYS = BIT(11), STA_FLG_DRAIN_FLOW = BIT(12), STA_FLG_PAN = BIT(13), STA_FLG_CLASS_AUTH = BIT(14), From f94045ed19821cee923f79953f3f35a2de81f695 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 6 Jan 2014 13:38:55 +0200 Subject: [PATCH 098/121] iwlwifi: mvm: reset Thermal Throttling's SMPS request upon disassociation The request of SMPS issued by the Thermal Throttling code was not reset when we disassociated - fix that. Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 797b4d91cfd7..e843080b5c0b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -944,6 +944,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_ERR(mvm, "failed to update power mode\n"); } iwl_mvm_bt_coex_vif_change(mvm); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, + IEEE80211_SMPS_AUTOMATIC); } else if (changes & BSS_CHANGED_BEACON_INFO) { /* * We received a beacon _after_ association so From e36b766d0c674c9dc73a2ace7aa37aea78afa233 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Mon, 6 Jan 2014 20:16:48 +0200 Subject: [PATCH 099/121] iwlwifi: change beamformee STS cap All beamformee supporting chips have the ability to support VHT NDP in up to 4 STSs. So change the published beamformee STS cap accordingly to 3 as it should be Nsts-1. Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 1b61cb529948..0c677b4e68cb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -264,14 +264,13 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, struct ieee80211_sta_vht_cap *vht_cap) { int num_ants = num_of_ant(data->valid_rx_ant); - int bf_sts_cap = num_ants - 1; vht_cap->vht_supported = true; vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | IEEE80211_VHT_CAP_RXSTBC_1 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | - bf_sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | + 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; if (num_ants > 1) From 65b30348dbf9b529901b5c2b62dca6cad9017a2a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Jan 2014 13:16:33 +0100 Subject: [PATCH 100/121] iwlwifi: add inline helper for packet lengths Add an inline helper function for getting an RX packet's length or payload length and use it throughout the code (most of which I did using an spatch.) While at it, adjust some code, and remove a bogus comment from the dvm calibration code. Signed-off-by: Johannes Berg Reviewed-by: Eran Harary Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 3 +-- drivers/net/wireless/iwlwifi/dvm/rx.c | 7 ++----- drivers/net/wireless/iwlwifi/dvm/ucode.c | 7 +------ drivers/net/wireless/iwlwifi/iwl-trans.h | 10 ++++++++++ drivers/net/wireless/iwlwifi/mvm/d3.c | 15 ++++++--------- drivers/net/wireless/iwlwifi/mvm/time-event.c | 4 ++-- drivers/net/wireless/iwlwifi/mvm/utils.c | 4 ++-- drivers/net/wireless/iwlwifi/pcie/rx.c | 2 +- 8 files changed, 25 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 8a3286a2bb31..e2ba4a725b34 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -407,9 +407,8 @@ static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait, { struct iwl_resume_data *resume_data = data; struct iwl_priv *priv = resume_data->priv; - u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (len - 4 != sizeof(*resume_data->cmd)) { + if (iwl_rx_packet_payload_len(pkt) != sizeof(*resume_data->cmd)) { IWL_ERR(priv, "rx wrong size data\n"); return true; } diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index b68bb2f4d2c2..7a1bc1c547e1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -205,8 +205,7 @@ static int iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 __maybe_unused len = - le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + u32 __maybe_unused len = iwl_rx_packet_len(pkt); IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " "notification for PM_DEBUG_STATISTIC_NOTIFIC:\n", len); iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->data, len); @@ -457,7 +456,7 @@ static int iwlagn_rx_statistics(struct iwl_priv *priv, const int reg_recalib_period = 60; int change; struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + u32 len = iwl_rx_packet_payload_len(pkt); __le32 *flag; struct statistics_general_common *common; struct statistics_rx_non_phy *rx_non_phy; @@ -467,8 +466,6 @@ static int iwlagn_rx_statistics(struct iwl_priv *priv, struct statistics_tx *tx; struct statistics_bt_activity *bt_activity; - len -= sizeof(struct iwl_cmd_header); /* skip header */ - IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n", len); diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 4da6d90b0829..998063580705 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -389,7 +389,6 @@ static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, { struct iwl_priv *priv = data; struct iwl_calib_hdr *hdr; - int len; if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) { WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION); @@ -397,12 +396,8 @@ static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, } hdr = (struct iwl_calib_hdr *)pkt->data; - len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - /* reduce the size by the length field itself */ - len -= sizeof(__le32); - - if (iwl_calib_set(priv, hdr, len)) + if (iwl_calib_set(priv, hdr, iwl_rx_packet_payload_len(pkt))) IWL_ERR(priv, "Failed to record calibration data %d\n", hdr->op_code); diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 8d1b5ed3502a..1f065cf4a4ba 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -176,6 +176,16 @@ struct iwl_rx_packet { u8 data[]; } __packed; +static inline u32 iwl_rx_packet_len(const struct iwl_rx_packet *pkt) +{ + return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; +} + +static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) +{ + return iwl_rx_packet_len(pkt) - sizeof(pkt->hdr); +} + /** * enum CMD_MODE - how to send the host commands ? * diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index f04d2f4d80cd..f36a7ee0267f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -886,8 +886,7 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, if (err) return err; - size = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - size -= sizeof(cmd.resp_pkt->hdr); + size = iwl_rx_packet_payload_len(cmd.resp_pkt); if (size < sizeof(__le16)) { err = -EINVAL; } else { @@ -1211,9 +1210,8 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out; #ifdef CONFIG_IWLWIFI_DEBUGFS - len = le32_to_cpu(d3_cfg_cmd.resp_pkt->len_n_flags) & - FH_RSCSR_FRAME_SIZE_MSK; - if (len >= sizeof(u32) * 2) { + len = iwl_rx_packet_payload_len(d3_cfg_cmd.resp_pkt); + if (len >= sizeof(u32)) { mvm->d3_test_pme_ptr = le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data); } @@ -1668,8 +1666,8 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, else status_size = sizeof(struct iwl_wowlan_status_v4); - len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (len - sizeof(struct iwl_cmd_header) < status_size) { + len = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (len < status_size) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out_free_resp; } @@ -1704,8 +1702,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, status.wake_packet = status_v4->wake_packet; } - if (len - sizeof(struct iwl_cmd_header) != - status_size + ALIGN(status.wake_packet_bufsize, 4)) { + if (len != status_size + ALIGN(status.wake_packet_bufsize, 4)) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out_free_resp; } diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 50f3d7f560bc..b4c2abaa297b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -249,12 +249,12 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, container_of(notif_wait, struct iwl_mvm, notif_wait); struct iwl_mvm_time_event_data *te_data = data; struct iwl_time_event_resp *resp; - int resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + int resp_len = iwl_rx_packet_payload_len(pkt); if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) return true; - if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) { + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); return true; } diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 487d61b25359..a4a5e25623c3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -168,8 +168,8 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, goto out_free_resp; } - resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) { + resp_len = iwl_rx_packet_payload_len(pkt); + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { ret = -EIO; goto out_free_resp; } diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 1890ea29c264..08c23d497a02 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -615,7 +615,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, rxcb._offset, get_cmd_string(trans_pcie, pkt->hdr.cmd), pkt->hdr.cmd); - len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + len = iwl_rx_packet_len(pkt); len += sizeof(u32); /* account for status word */ trace_iwlwifi_dev_rx(trans->dev, trans, pkt, len); trace_iwlwifi_dev_rx_data(trans->dev, trans, pkt, len); From 05159fccf21cde603f7be8815a94ac85e9c0f245 Mon Sep 17 00:00:00 2001 From: Eytan Lifshitz Date: Tue, 7 Jan 2014 12:50:45 +0200 Subject: [PATCH 101/121] iwlwifi: mvm: fix theoretical uninitialized function return value If we try to write NVM that do not exist, the function will return uninitialized value. fixed. Signed-off-by: Eytan Lifshitz Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 9affd43db811..35b71af78d02 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -392,7 +392,7 @@ out: /* Loads the NVM data stored in mvm->nvm_sections into the NIC */ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) { - int i, ret; + int i, ret = 0; struct iwl_nvm_section *sections = mvm->nvm_sections; IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); From 7a06a38a115c4d8c1b442e544047fb151d5d4e03 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 9 Jan 2014 09:55:38 +0200 Subject: [PATCH 102/121] iwlwifi: mvm: add REPLY_SF_CFG_CMD to cmd strings Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 2437a06bf60e..a3d43de342d7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -309,6 +309,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(BT_PROFILE_NOTIFICATION), CMD(BT_CONFIG), CMD(MCAST_FILTER_CMD), + CMD(REPLY_SF_CFG_CMD), CMD(REPLY_BEACON_FILTERING_CMD), CMD(REPLY_THERMAL_MNG_BACKOFF), CMD(MAC_PM_POWER_TABLE), From cf38e4f756d6396657c76f72ef42b6f47c523f97 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sun, 5 Jan 2014 19:27:24 +0200 Subject: [PATCH 103/121] iwlwifi: mvm: don't use highest rate in VHT MCS Set Keeping this as 0 is ok according to spec section 9.7.11 as this means the limits are according to the Tx/Rx supported MCS x NSS bitmap. Initially we've set these as there were concerns of interop issues but these turned out to be false. Signed-off-by: Eyal Shapira Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 0c677b4e68cb..f06f4cbe1317 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -289,9 +289,6 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); - /* Max rate for Long GI NSS=2 80Mhz is 780Mbps */ - vht_cap->vht_mcs.rx_highest = cpu_to_le16(780); - if (num_ants == 1 || cfg->rx_with_siso_diversity) { vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | @@ -299,12 +296,9 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, /* this works because NOT_SUPPORTED == 3 */ vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); - /* Max rate for Long GI NSS=1 80Mhz is 390Mbps */ - vht_cap->vht_mcs.rx_highest = cpu_to_le16(390); } vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; - vht_cap->vht_mcs.tx_highest = vht_cap->vht_mcs.rx_highest; } static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, From a74d036f98280b0de312f842ad09c26de56f27c5 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Mon, 13 Jan 2014 22:20:22 +0100 Subject: [PATCH 104/121] brcmfmac: Create common nvram parsing routines. New bus layers like pcie require nvram parsing routines which are the same routines as being used by sdio. Make these routines common in the new file nvram.c. Update sdio to use these routines and simplify the nvram upload process. Also add memory validation check for downloaded firmware and nvram in debug mode. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/Makefile | 1 + .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 166 +++++++++--------- .../net/wireless/brcm80211/brcmfmac/nvram.c | 94 ++++++++++ .../net/wireless/brcm80211/brcmfmac/nvram.h | 24 +++ .../wireless/brcm80211/brcmfmac/sdio_chip.c | 110 +----------- .../wireless/brcm80211/brcmfmac/sdio_chip.h | 3 +- 6 files changed, 208 insertions(+), 190 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/nvram.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/nvram.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 5681b9862023..57cddee03252 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -32,6 +32,7 @@ brcmfmac-objs += \ bcdc.o \ dhd_common.o \ dhd_linux.o \ + nvram.o \ btcoex.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 9c7f08a13105..4f936c6b682d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -41,6 +41,7 @@ #include #include "sdio_host.h" #include "sdio_chip.h" +#include "nvram.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ @@ -369,8 +370,6 @@ struct brcmf_sdio_hdrinfo { struct brcmf_sdio { struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ struct chip_info *ci; /* Chip info struct */ - char *vars; /* Variables (from CIS and/or other) */ - uint varsz; /* Size of variables buffer */ u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ @@ -3207,8 +3206,7 @@ static bool brcmf_sdio_download_state(struct brcmf_sdio *bus, bool enter) brcmf_sdio_chip_enter_download(bus->sdiodev, ci); } else { - if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci, bus->vars, - bus->varsz)) + if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci)) return false; /* Allow HT Clock now that the ARM is running. */ @@ -3220,6 +3218,60 @@ static bool brcmf_sdio_download_state(struct brcmf_sdio *bus, bool enter) return true; } +#ifdef DEBUG +static bool +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, + u8 *ram_data, uint ram_sz) +{ + char *ram_cmp; + int err; + bool ret = true; + int address; + int offset; + int len; + + /* read back and verify */ + brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr, + ram_sz); + ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL); + /* do not proceed while no memory but */ + if (!ram_cmp) + return true; + + address = ram_addr; + offset = 0; + while (offset < ram_sz) { + len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK : + ram_sz - offset; + err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len); + if (err) { + brcmf_err("error %d on reading %d membytes at 0x%08x\n", + err, len, address); + ret = false; + break; + } else if (memcmp(ram_cmp, &ram_data[offset], len)) { + brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n", + offset, len); + ret = false; + break; + } + offset += len; + address += len; + } + + kfree(ram_cmp); + + return ret; +} +#else /* DEBUG */ +static bool +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, + u8 *ram_data, uint ram_sz) +{ + return true; +} +#endif /* DEBUG */ + static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus) { const struct firmware *fw; @@ -3228,6 +3280,8 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus) int address; int len; + brcmf_dbg(TRACE, "Enter\n"); + fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); if (fw == NULL) return -ENOENT; @@ -3252,6 +3306,10 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus) offset += len; address += len; } + if (!err) + if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, + (u8 *)fw->data, fw->size)) + err = -EIO; failure: release_firmware(fw); @@ -3259,94 +3317,37 @@ failure: return err; } -/* - * ProcessVars:Takes a buffer of "=\n" lines read from a file - * and ending in a NUL. - * Removes carriage returns, empty lines, comment lines, and converts - * newlines to NULs. - * Shortens buffer as needed and pads with NULs. End of buffer is marked - * by two NULs. -*/ - -static int brcmf_sdio_strip_nvram(struct brcmf_sdio *bus, - const struct firmware *nv) -{ - char *varbuf; - char *dp; - bool findNewline; - int column; - int ret = 0; - uint buf_len, n, len; - - len = nv->size; - varbuf = vmalloc(len); - if (!varbuf) - return -ENOMEM; - - memcpy(varbuf, nv->data, len); - dp = varbuf; - - findNewline = false; - column = 0; - - for (n = 0; n < len; n++) { - if (varbuf[n] == 0) - break; - if (varbuf[n] == '\r') - continue; - if (findNewline && varbuf[n] != '\n') - continue; - findNewline = false; - if (varbuf[n] == '#') { - findNewline = true; - continue; - } - if (varbuf[n] == '\n') { - if (column == 0) - continue; - *dp++ = 0; - column = 0; - continue; - } - *dp++ = varbuf[n]; - column++; - } - buf_len = dp - varbuf; - while (dp < varbuf + n) - *dp++ = 0; - - kfree(bus->vars); - /* roundup needed for download to device */ - bus->varsz = roundup(buf_len + 1, 4); - bus->vars = kmalloc(bus->varsz, GFP_KERNEL); - if (bus->vars == NULL) { - bus->varsz = 0; - ret = -ENOMEM; - goto err; - } - - /* copy the processed variables and add null termination */ - memcpy(bus->vars, varbuf, buf_len); - bus->vars[buf_len] = 0; -err: - vfree(varbuf); - return ret; -} - static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus) { const struct firmware *nv; - int ret; + void *vars; + u32 varsz; + int address; + int err; + + brcmf_dbg(TRACE, "Enter\n"); nv = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM); if (nv == NULL) return -ENOENT; - ret = brcmf_sdio_strip_nvram(bus, nv); - + vars = brcmf_nvram_strip(nv, &varsz); release_firmware(nv); - return ret; + if (vars == NULL) + return -EINVAL; + + address = bus->ci->ramsize - varsz + bus->ci->rambase; + err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); + if (err) + brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", + err, varsz, address); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) + err = -EIO; + + brcmf_nvram_free(vars); + + return err; } static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) @@ -4092,7 +4093,6 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) brcmu_pkt_buf_free_skb(bus->txglom_sgpad); kfree(bus->rxbuf); kfree(bus->hdrbuf); - kfree(bus->vars); kfree(bus); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c b/drivers/net/wireless/brcm80211/brcmfmac/nvram.c new file mode 100644 index 000000000000..d5ef86db631b --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/nvram.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "nvram.h" + +/* brcmf_nvram_strip :Takes a buffer of "=\n" lines read from a file + * and ending in a NUL. Removes carriage returns, empty lines, comment lines, + * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. + * End of buffer is completed with token identifying length of buffer. + */ +void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length) +{ + u8 *nvram; + u32 i; + u32 len; + u32 column; + u8 val; + bool comment; + u32 token; + __le32 token_le; + + /* Alloc for extra 0 byte + roundup by 4 + length field */ + nvram = kmalloc(nv->size + 1 + 3 + sizeof(token_le), GFP_KERNEL); + if (!nvram) + return NULL; + + len = 0; + column = 0; + comment = false; + for (i = 0; i < nv->size; i++) { + val = nv->data[i]; + if (val == 0) + break; + if (val == '\r') + continue; + if (comment && (val != '\n')) + continue; + comment = false; + if (val == '#') { + comment = true; + continue; + } + if (val == '\n') { + if (column == 0) + continue; + nvram[len] = 0; + len++; + column = 0; + continue; + } + nvram[len] = val; + len++; + column++; + } + column = len; + *new_length = roundup(len + 1, 4); + while (column != *new_length) { + nvram[column] = 0; + column++; + } + + token = *new_length / 4; + token = (~token << 16) | (token & 0x0000FFFF); + token_le = cpu_to_le32(token); + + memcpy(&nvram[*new_length], &token_le, sizeof(token_le)); + *new_length += sizeof(token_le); + + return nvram; +} + +void brcmf_nvram_free(void *nvram) +{ + kfree(nvram); +} + + diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.h b/drivers/net/wireless/brcm80211/brcmfmac/nvram.h new file mode 100644 index 000000000000..d454580928c9 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/nvram.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_NVRAM_H +#define BRCMFMAC_NVRAM_H + + +void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length); +void brcmf_nvram_free(void *nvram); + + +#endif /* BRCMFMAC_NVRAM_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index 9fd40675f18e..a74a3d1c3e00 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -842,107 +842,16 @@ brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, } } -#ifdef DEBUG -static bool -brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr, - char *nvram_dat, uint nvram_sz) -{ - char *nvram_ularray; - int err; - bool ret = true; - - /* read back and verify */ - brcmf_dbg(INFO, "Compare NVRAM dl & ul; size=%d\n", nvram_sz); - nvram_ularray = kmalloc(nvram_sz, GFP_KERNEL); - /* do not proceed while no memory but */ - if (!nvram_ularray) - return true; - - /* Upload image to verify downloaded contents. */ - memset(nvram_ularray, 0xaa, nvram_sz); - - /* Read the vars list to temp buffer for comparison */ - err = brcmf_sdiod_ramrw(sdiodev, false, nvram_addr, nvram_ularray, - nvram_sz); - if (err) { - brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n", - err, nvram_sz, nvram_addr); - } else if (memcmp(nvram_dat, nvram_ularray, nvram_sz)) { - brcmf_err("Downloaded NVRAM image is corrupted\n"); - ret = false; - } - kfree(nvram_ularray); - - return ret; -} -#else /* DEBUG */ -static inline bool -brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr, - char *nvram_dat, uint nvram_sz) -{ - return true; -} -#endif /* DEBUG */ - -static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, - char *nvram_dat, uint nvram_sz) -{ - int err; - u32 nvram_addr; - u32 token; - __le32 token_le; - - nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase; - - /* Write the vars list */ - err = brcmf_sdiod_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz); - if (err) { - brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", - err, nvram_sz, nvram_addr); - return false; - } - - if (!brcmf_sdio_chip_verifynvram(sdiodev, nvram_addr, - nvram_dat, nvram_sz)) - return false; - - /* generate token: - * nvram size, converted to words, in lower 16-bits, checksum - * in upper 16-bits. - */ - token = nvram_sz / 4; - token = (~token << 16) | (token & 0x0000FFFF); - token_le = cpu_to_le32(token); - - brcmf_dbg(INFO, "RAM size: %d\n", ci->ramsize); - brcmf_dbg(INFO, "nvram is placed at %d, size %d, token=0x%08x\n", - nvram_addr, nvram_sz, token); - - /* Write the length token to the last word */ - if (brcmf_sdiod_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase), - (u8 *)&token_le, 4)) - return false; - - return true; -} - static void brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) { - u32 zeros = 0; - ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0); - - /* clear length token */ - brcmf_sdiod_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4); } static bool -brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, - char *nvram_dat, uint nvram_sz) +brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) { u8 core_idx; u32 reg_addr; @@ -952,9 +861,6 @@ brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, return false; } - if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz)) - return false; - /* clear all interrupts */ core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); reg_addr = ci->c_inf[core_idx].base; @@ -975,15 +881,11 @@ brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, } static bool -brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, - char *nvram_dat, uint nvram_sz) +brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) { u8 core_idx; u32 reg_addr; - if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz)) - return false; - /* clear all interrupts */ core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); reg_addr = ci->c_inf[core_idx].base; @@ -1015,15 +917,13 @@ void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, } bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, char *nvram_dat, - uint nvram_sz) + struct chip_info *ci) { u8 arm_core_idx; arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); if (BRCMF_MAX_CORENUM != arm_core_idx) - return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat, - nvram_sz); + return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci); - return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, nvram_dat, nvram_sz); + return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h index 7ea424e20773..c7d0dbc1ab59 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -224,7 +224,6 @@ u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid); void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci); bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, char *nvram_dat, - uint nvram_sz); + struct chip_info *ci); #endif /* _BRCMFMAC_SDIO_CHIP_H_ */ From 53036261033abdab7323a4a896a9bde84d2c2f17 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Mon, 13 Jan 2014 22:20:23 +0100 Subject: [PATCH 105/121] brcmfmac: update core reset and disable routines. The original core reset and disable routines do not work always on running system. These routines were updated to properly reset a core. When module is unloaded the device is put into download state where all necessary cores have been reset. This will make sure the device is in idle mode after module unload. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 8 +- .../wireless/brcm80211/brcmfmac/sdio_chip.c | 149 +++++++++++------- .../wireless/brcm80211/brcmfmac/sdio_chip.h | 6 +- 3 files changed, 106 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 4f936c6b682d..19057896d92e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2263,8 +2263,6 @@ static void brcmf_sdio_bus_stop(struct device *dev) w_sdreg32(bus, local_hostintmask, offsetof(struct sdpcmd_regs, intstatus)); - /* Turn off the backplane clock (only) */ - brcmf_sdio_clkctl(bus, CLK_SDONLY, false); sdio_release_host(bus->sdiodev->func[1]); /* Clear the data packet queues */ @@ -4085,6 +4083,12 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) if (bus->ci) { sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + /* Leave the device in state where it is 'quiet'. This + * is done by putting it in download_state which + * essentially resets all necessary cores + */ + msleep(20); + brcmf_sdio_download_state(bus, true); brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func[1]); brcmf_sdio_chip_detach(&bus->ci); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index a74a3d1c3e00..434297648e34 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -51,6 +51,9 @@ #define BCM43143_CORE_ARM_BASE 0x18003000 #define BCM43143_RAMSIZE 0x70000 +/* All D11 cores, ID 0x812 */ +#define BCM43xx_CORE_D11_BASE 0x18001000 + #define SBCOREREV(sbidh) \ ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ ((sbidh) & SSB_IDHIGH_RCLO)) @@ -66,6 +69,10 @@ /* ARM CR4 core specific control flag bits */ #define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 +/* D11 core specific control flag bits */ +#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 +#define D11_BCMA_IOCTL_PHYRESET 0x0008 + #define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) /* SDIO Pad drive strength to select value mappings */ struct sdiod_drive_str { @@ -193,7 +200,8 @@ brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, static void brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits) + struct chip_info *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits) { u32 regdata, base; u8 idx; @@ -279,52 +287,48 @@ brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, static void brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits) + struct chip_info *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits) { u8 idx; u32 regdata; + u32 wrapbase; idx = brcmf_sdio_chip_getinfidx(ci, coreid); if (idx == BRCMF_MAX_CORENUM) return; + wrapbase = ci->c_inf[idx].wrapbase; + /* if core is already in reset, just return */ - regdata = brcmf_sdiod_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL); if ((regdata & BCMA_RESET_CTL_RESET) != 0) return; - /* ensure no pending backplane operation - * 300uc should be sufficient for backplane ops to be finish - * extra 10ms is taken into account for firmware load stage - * after 10300us carry on disabling the core anyway - */ - SPINWAIT(brcmf_sdiod_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_ST, - NULL), 10300); - regdata = brcmf_sdiod_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_ST, - NULL); - if (regdata) - brcmf_err("disabling core 0x%x with reset status %x\n", - coreid, regdata); + /* configure reset */ + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits | + BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); - brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, + /* put in reset */ + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, BCMA_RESET_CTL_RESET, NULL); - udelay(1); - - brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); usleep_range(10, 20); + /* wait till reset is 1 */ + SPINWAIT(brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) != + BCMA_RESET_CTL_RESET, 300); + + /* post reset configure */ + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits | + BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); } static void brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits) + struct chip_info *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits, u32 post_resetbits) { u32 regdata; u8 idx; @@ -337,7 +341,8 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, * Must do the disable sequence first to work for * arbitrary current core state. */ - brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, 0); + brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, pre_resetbits, + in_resetbits); /* * Now do the initialization sequence. @@ -390,35 +395,32 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, static void brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits) + struct chip_info *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits, u32 post_resetbits) { u8 idx; u32 regdata; + u32 wrapbase; idx = brcmf_sdio_chip_getinfidx(ci, coreid); if (idx == BRCMF_MAX_CORENUM) return; + wrapbase = ci->c_inf[idx].wrapbase; + /* must disable first to work for arbitrary current core state */ - brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits); + brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, pre_resetbits, + in_resetbits); - /* now do initialization sequence */ - brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); - brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - 0, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); - udelay(1); + while (brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) & + BCMA_RESET_CTL_RESET) { + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, 0, NULL); + usleep_range(40, 60); + } - brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); - udelay(1); + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, post_resetbits | + BCMA_IOCTL_CLK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); } #ifdef DEBUG @@ -498,6 +500,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[3].base = BCM43143_CORE_ARM_BASE; ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; ci->c_inf[3].cib = 0x07000000; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; ci->ramsize = BCM43143_RAMSIZE; break; case BCM43241_CHIP_ID: @@ -515,6 +520,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[3].base = 0x18003000; ci->c_inf[3].wrapbase = 0x18103000; ci->c_inf[3].cib = 0x07004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; ci->ramsize = 0x90000; break; case BCM4329_CHIP_ID: @@ -524,6 +532,8 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; ci->c_inf[3].id = BCMA_CORE_ARM_CM3; ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; ci->ramsize = BCM4329_RAMSIZE; break; case BCM4330_CHIP_ID: @@ -541,6 +551,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[3].base = 0x18003000; ci->c_inf[3].wrapbase = 0x18103000; ci->c_inf[3].cib = 0x03004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; ci->ramsize = 0x48000; break; case BCM4334_CHIP_ID: @@ -558,6 +571,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[3].base = 0x18003000; ci->c_inf[3].wrapbase = 0x18103000; ci->c_inf[3].cib = 0x07004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; ci->ramsize = 0x80000; break; case BCM4335_CHIP_ID: @@ -571,6 +587,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[2].base = 0x18002000; ci->c_inf[2].wrapbase = 0x18102000; ci->c_inf[2].cib = 0x01084411; + ci->c_inf[3].id = BCMA_CORE_80211; + ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; ci->ramsize = 0xc0000; ci->rambase = 0x180000; break; @@ -585,6 +604,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[2].base = 0x18002000; ci->c_inf[2].wrapbase = 0x18102000; ci->c_inf[2].cib = 0x04084411; + ci->c_inf[3].id = BCMA_CORE_80211; + ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; ci->ramsize = 0xc0000; ci->rambase = 0x180000; break; @@ -603,6 +625,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[3].base = 0x18003000; ci->c_inf[3].wrapbase = 0x18103000; ci->c_inf[3].cib = 0x03004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; ci->ramsize = 0x3C000; break; default: @@ -713,7 +738,7 @@ brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, * Make sure any on-chip ARM is off (in case strapping is wrong), * or downloaded code was already running. */ - ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); + ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0); } int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, @@ -846,8 +871,11 @@ static void brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) { - ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); - ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0); + ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0); + ci->resetcore(sdiodev, ci, BCMA_CORE_80211, + D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); + ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0, 0, 0); } static bool @@ -867,7 +895,7 @@ brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) reg_addr += offsetof(struct sdpcmd_regs, intstatus); brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); + ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0, 0); return true; } @@ -876,8 +904,22 @@ static inline void brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) { - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, - ARMCR4_BCMA_IOCTL_CPUHALT); + u8 idx; + u32 regdata; + u32 wrapbase; + idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4); + + if (idx == BRCMF_MAX_CORENUM) + return; + + wrapbase = ci->c_inf[idx].wrapbase; + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); + regdata &= ARMCR4_BCMA_IOCTL_CPUHALT; + ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, regdata, + ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT); + ci->resetcore(sdiodev, ci, BCMA_CORE_80211, + D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); } static bool @@ -897,7 +939,8 @@ brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) sizeof(ci->rst_vec)); /* restore ARM */ - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0); + ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, ARMCR4_BCMA_IOCTL_CPUHALT, + 0, 0); return true; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h index c7d0dbc1ab59..91c61cb9de87 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -81,9 +81,11 @@ struct chip_info { u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u16 coreid); void (*coredisable)(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits); + struct chip_info *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits); void (*resetcore)(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits); + struct chip_info *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits, u32 post_resetbits); }; struct sbconfig { From 3355650c61c4e3d56deadb601762b09ed8a30939 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:25 +0100 Subject: [PATCH 106/121] brcmfmac: rework firmware download code The firmware download code has been restructured so the reset vector does not need to be stored in a structure, but keep it on the stack to be passed to exit download function. Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 87 +++++++------------ .../wireless/brcm80211/brcmfmac/sdio_chip.c | 11 +-- .../wireless/brcm80211/brcmfmac/sdio_chip.h | 2 +- 3 files changed, 40 insertions(+), 60 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 19057896d92e..b9d5f735a71b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3192,30 +3192,6 @@ brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) return rxlen ? (int)rxlen : -ETIMEDOUT; } -static bool brcmf_sdio_download_state(struct brcmf_sdio *bus, bool enter) -{ - struct chip_info *ci = bus->ci; - - /* To enter download state, disable ARM and reset SOCRAM. - * To exit download state, simply reset ARM (default is RAM boot). - */ - if (enter) { - bus->alp_only = true; - - brcmf_sdio_chip_enter_download(bus->sdiodev, ci); - } else { - if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci)) - return false; - - /* Allow HT Clock now that the ARM is running. */ - bus->alp_only = false; - - bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD; - } - - return true; -} - #ifdef DEBUG static bool brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, @@ -3270,9 +3246,9 @@ brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, } #endif /* DEBUG */ -static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus) +static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, + const struct firmware *fw) { - const struct firmware *fw; int err; int offset; int address; @@ -3280,14 +3256,6 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); - fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); - if (fw == NULL) - return -ENOENT; - - if (brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4) != - BRCMF_MAX_CORENUM) - memcpy(&bus->ci->rst_vec, fw->data, sizeof(bus->ci->rst_vec)); - err = 0; offset = 0; address = bus->ci->rambase; @@ -3299,7 +3267,7 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus) if (err) { brcmf_err("error %d on writing %d membytes at 0x%08x\n", err, len, address); - goto failure; + return err; } offset += len; address += len; @@ -3309,15 +3277,12 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus) (u8 *)fw->data, fw->size)) err = -EIO; -failure: - release_firmware(fw); - return err; } -static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus) +static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, + const struct firmware *nv) { - const struct firmware *nv; void *vars; u32 varsz; int address; @@ -3325,12 +3290,7 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); - nv = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM); - if (nv == NULL) - return -ENOENT; - vars = brcmf_nvram_strip(nv, &varsz); - release_firmware(nv); if (vars == NULL) return -EINVAL; @@ -3351,33 +3311,52 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus) static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) { int bcmerror = -EFAULT; - + const struct firmware *fw; + u32 rstvec; sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Keep arm in reset */ - if (!brcmf_sdio_download_state(bus, true)) { - brcmf_err("error placing ARM core in reset\n"); + brcmf_sdio_chip_enter_download(bus->sdiodev, bus->ci); + + fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); + if (fw == NULL) { + bcmerror = -ENOENT; goto err; } - if (brcmf_sdio_download_code_file(bus)) { + rstvec = get_unaligned_le32(fw->data); + brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); + + bcmerror = brcmf_sdio_download_code_file(bus, fw); + release_firmware(fw); + if (bcmerror) { brcmf_err("dongle image file download failed\n"); goto err; } - if (brcmf_sdio_download_nvram(bus)) { + fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM); + if (fw == NULL) { + bcmerror = -ENOENT; + goto err; + } + + bcmerror = brcmf_sdio_download_nvram(bus, fw); + release_firmware(fw); + if (bcmerror) { brcmf_err("dongle nvram file download failed\n"); goto err; } /* Take arm out of reset */ - if (!brcmf_sdio_download_state(bus, false)) { + if (!brcmf_sdio_chip_exit_download(bus->sdiodev, bus->ci, rstvec)) { brcmf_err("error getting out of ARM core reset\n"); goto err; } + /* Allow HT Clock now that the ARM is running. */ + bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD; bcmerror = 0; err: @@ -3566,9 +3545,11 @@ static int brcmf_sdio_bus_init(struct device *dev) /* try to download image and nvram to the dongle */ if (bus_if->state == BRCMF_BUS_DOWN) { + bus->alp_only = true; err = brcmf_sdio_download_firmware(bus); if (err) return err; + bus->alp_only = false; } if (!bus->sdiodev->bus_if->drvr) @@ -3778,8 +3759,6 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) u32 reg_val; u32 drivestrength; - bus->alp_only = true; - sdio_claim_host(bus->sdiodev->func[1]); pr_debug("F1 signature read @0x18000000=0x%4x\n", @@ -4088,7 +4067,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) * essentially resets all necessary cores */ msleep(20); - brcmf_sdio_download_state(bus, true); + brcmf_sdio_chip_enter_download(bus->sdiodev, bus->ci); brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func[1]); brcmf_sdio_chip_detach(&bus->ci); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index 434297648e34..88eacf16211a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -923,7 +923,8 @@ brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, } static bool -brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) +brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, + u32 rstvec) { u8 core_idx; u32 reg_addr; @@ -935,8 +936,8 @@ brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); /* Write reset vector to address 0 */ - brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec, - sizeof(ci->rst_vec)); + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, + sizeof(rstvec)); /* restore ARM */ ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, ARMCR4_BCMA_IOCTL_CPUHALT, @@ -960,7 +961,7 @@ void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, } bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) + struct chip_info *ci, u32 rstvec) { u8 arm_core_idx; @@ -968,5 +969,5 @@ bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, if (BRCMF_MAX_CORENUM != arm_core_idx) return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci); - return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci); + return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, rstvec); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h index 91c61cb9de87..9a72b71f5259 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -226,6 +226,6 @@ u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid); void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci); bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci); + struct chip_info *ci, u32 rstvec); #endif /* _BRCMFMAC_SDIO_CHIP_H_ */ From c805eeb7a8e165e218042a747ab41c2dbdc74885 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:26 +0100 Subject: [PATCH 107/121] brcmfmac: restructure brcmf_sdio_chip_recognition() Rework function to allow only bcm4329 in case of chip backplane type being sonics sillicon backplane. Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/sdio_chip.c | 333 +++++++++--------- .../wireless/brcm80211/brcmfmac/sdio_chip.h | 1 - 2 files changed, 167 insertions(+), 167 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index 88eacf16211a..d1de037994cb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -462,70 +462,38 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) { u32 regdata; - int ret; + u32 socitype; /* Get CC core rev - * Chipid is assume to be at offset 0 from regs arg + * Chipid is assume to be at offset 0 from SI_ENUM_BASE * For different chiptypes or old sdio hosts w/o chipcommon, * other ways of recognition should be added here. */ - ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; - ci->c_inf[0].base = SI_ENUM_BASE; regdata = brcmf_sdiod_regrl(sdiodev, - CORE_CC_REG(ci->c_inf[0].base, chipid), + CORE_CC_REG(SI_ENUM_BASE, chipid), NULL); ci->chip = regdata & CID_ID_MASK; ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && ci->chiprev >= 2) ci->chip = BCM4339_CHIP_ID; - ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; + socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; - brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); + brcmf_dbg(INFO, "found %s chip: id=0x%x, rev=%d\n", + socitype == SOCI_SB ? "SB" : "AXI", ci->chip, ci->chiprev); - /* Address of cores for new chips should be added here */ - switch (ci->chip) { - case BCM43143_CHIP_ID: - ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000; - ci->c_inf[0].cib = 0x2b000000; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = BCM43143_CORE_BUS_BASE; - ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000; - ci->c_inf[1].cib = 0x18000000; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE; - ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000; - ci->c_inf[2].cib = 0x14000000; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = BCM43143_CORE_ARM_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->c_inf[3].cib = 0x07000000; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = BCM43143_RAMSIZE; - break; - case BCM43241_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2a084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0e004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x14080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x07004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x90000; - break; - case BCM4329_CHIP_ID: + if (socitype == SOCI_SB) { + if (ci->chip != BCM4329_CHIP_ID) { + brcmf_err("SB chip is not supported\n"); + return -ENODEV; + } + ci->iscoreup = brcmf_sdio_sb_iscoreup; + ci->corerev = brcmf_sdio_sb_corerev; + ci->coredisable = brcmf_sdio_sb_coredisable; + ci->resetcore = brcmf_sdio_sb_resetcore; + + ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; + ci->c_inf[0].base = SI_ENUM_BASE; ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; @@ -535,129 +503,162 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[4].id = BCMA_CORE_80211; ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; ci->ramsize = BCM4329_RAMSIZE; - break; - case BCM4330_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x27004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x07004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x0d080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x03004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x48000; - break; - case BCM4334_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x29004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0d004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x13080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x07004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x80000; - break; - case BCM4335_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2b084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18005000; - ci->c_inf[1].wrapbase = 0x18105000; - ci->c_inf[1].cib = 0x0f004211; - ci->c_inf[2].id = BCMA_CORE_ARM_CR4; - ci->c_inf[2].base = 0x18002000; - ci->c_inf[2].wrapbase = 0x18102000; - ci->c_inf[2].cib = 0x01084411; - ci->c_inf[3].id = BCMA_CORE_80211; - ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->ramsize = 0xc0000; - ci->rambase = 0x180000; - break; - case BCM4339_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2e084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18005000; - ci->c_inf[1].wrapbase = 0x18105000; - ci->c_inf[1].cib = 0x15004211; - ci->c_inf[2].id = BCMA_CORE_ARM_CR4; - ci->c_inf[2].base = 0x18002000; - ci->c_inf[2].wrapbase = 0x18102000; - ci->c_inf[2].cib = 0x04084411; - ci->c_inf[3].id = BCMA_CORE_80211; - ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->ramsize = 0xc0000; - ci->rambase = 0x180000; - break; - case BCM43362_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x27004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0a004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x08080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x03004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x3C000; - break; - default: - brcmf_err("chipid 0x%x is not supported\n", ci->chip); - return -ENODEV; - } - - ret = brcmf_sdio_chip_cichk(ci); - if (ret) - return ret; - - switch (ci->socitype) { - case SOCI_SB: - ci->iscoreup = brcmf_sdio_sb_iscoreup; - ci->corerev = brcmf_sdio_sb_corerev; - ci->coredisable = brcmf_sdio_sb_coredisable; - ci->resetcore = brcmf_sdio_sb_resetcore; - break; - case SOCI_AI: + } else if (socitype == SOCI_AI) { ci->iscoreup = brcmf_sdio_ai_iscoreup; ci->corerev = brcmf_sdio_ai_corerev; ci->coredisable = brcmf_sdio_ai_coredisable; ci->resetcore = brcmf_sdio_ai_resetcore; - break; - default: - brcmf_err("socitype %u not supported\n", ci->socitype); + + ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; + ci->c_inf[0].base = SI_ENUM_BASE; + + /* Address of cores for new chips should be added here */ + switch (ci->chip) { + case BCM43143_CHIP_ID: + ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000; + ci->c_inf[0].cib = 0x2b000000; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = BCM43143_CORE_BUS_BASE; + ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000; + ci->c_inf[1].cib = 0x18000000; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE; + ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000; + ci->c_inf[2].cib = 0x14000000; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = BCM43143_CORE_ARM_BASE; + ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; + ci->c_inf[3].cib = 0x07000000; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = BCM43143_RAMSIZE; + break; + case BCM43241_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x2a084411; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x0e004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x14080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x07004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = 0x90000; + break; + case BCM4330_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x27004211; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x07004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x0d080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x03004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = 0x48000; + break; + case BCM4334_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x29004211; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x0d004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x13080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x07004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = 0x80000; + break; + case BCM4335_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x2b084411; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18005000; + ci->c_inf[1].wrapbase = 0x18105000; + ci->c_inf[1].cib = 0x0f004211; + ci->c_inf[2].id = BCMA_CORE_ARM_CR4; + ci->c_inf[2].base = 0x18002000; + ci->c_inf[2].wrapbase = 0x18102000; + ci->c_inf[2].cib = 0x01084411; + ci->c_inf[3].id = BCMA_CORE_80211; + ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; + ci->ramsize = 0xc0000; + ci->rambase = 0x180000; + break; + case BCM43362_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x27004211; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x0a004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x08080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x03004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = 0x3C000; + break; + case BCM4339_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x2e084411; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18005000; + ci->c_inf[1].wrapbase = 0x18105000; + ci->c_inf[1].cib = 0x15004211; + ci->c_inf[2].id = BCMA_CORE_ARM_CR4; + ci->c_inf[2].base = 0x18002000; + ci->c_inf[2].wrapbase = 0x18102000; + ci->c_inf[2].cib = 0x04084411; + ci->c_inf[3].id = BCMA_CORE_80211; + ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; + ci->ramsize = 0xc0000; + ci->rambase = 0x180000; + break; + default: + brcmf_err("AXI chip is not supported\n"); + return -ENODEV; + } + } else { + brcmf_err("chip backplane type %u is not supported\n", + socitype); return -ENODEV; } - return 0; + return brcmf_sdio_chip_cichk(ci); } static int diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h index 9a72b71f5259..a1aadf419536 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -66,7 +66,6 @@ struct chip_core_info { struct chip_info { u32 chip; u32 chiprev; - u32 socitype; /* core info */ /* always put chipcommon core at 0, bus core at 1 */ struct chip_core_info c_inf[BRCMF_MAX_CORENUM]; From 9cf218fc333406764275eab9b0e47f5c39929e79 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:27 +0100 Subject: [PATCH 108/121] brcmfmac: rename chip and core related structures Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- .../wireless/brcm80211/brcmfmac/sdio_chip.c | 54 +++++++++---------- .../wireless/brcm80211/brcmfmac/sdio_chip.h | 27 +++++----- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index b9d5f735a71b..ef24a1bd24a7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -369,7 +369,7 @@ struct brcmf_sdio_hdrinfo { /* Private data for SDIO bus interaction */ struct brcmf_sdio { struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ - struct chip_info *ci; /* Chip info struct */ + struct brcmf_chip *ci; /* Chip info struct */ u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index d1de037994cb..82bf3c5d3cdc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -118,7 +118,7 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { }; u8 -brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid) +brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid) { u8 idx; @@ -131,7 +131,7 @@ brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid) static u32 brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid) + struct brcmf_chip *ci, u16 coreid) { u32 regdata; u8 idx; @@ -146,7 +146,7 @@ brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, static u32 brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid) + struct brcmf_chip *ci, u16 coreid) { u8 idx; @@ -157,7 +157,7 @@ brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, static bool brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid) + struct brcmf_chip *ci, u16 coreid) { u32 regdata; u8 idx; @@ -176,7 +176,7 @@ brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, static bool brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid) + struct brcmf_chip *ci, u16 coreid) { u32 regdata; u8 idx; @@ -200,7 +200,7 @@ brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, static void brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 pre_resetbits, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, u32 in_resetbits) { u32 regdata, base; @@ -287,7 +287,7 @@ brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, static void brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 pre_resetbits, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, u32 in_resetbits) { u8 idx; @@ -327,7 +327,7 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, static void brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 pre_resetbits, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, u32 in_resetbits, u32 post_resetbits) { u32 regdata; @@ -395,7 +395,7 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, static void brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 pre_resetbits, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, u32 in_resetbits, u32 post_resetbits) { u8 idx; @@ -425,7 +425,7 @@ brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, #ifdef DEBUG /* safety check for chipinfo */ -static int brcmf_sdio_chip_cichk(struct chip_info *ci) +static int brcmf_sdio_chip_cichk(struct brcmf_chip *ci) { u8 core_idx; @@ -452,14 +452,14 @@ static int brcmf_sdio_chip_cichk(struct chip_info *ci) return 0; } #else /* DEBUG */ -static inline int brcmf_sdio_chip_cichk(struct chip_info *ci) +static inline int brcmf_sdio_chip_cichk(struct brcmf_chip *ci) { return 0; } #endif static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) + struct brcmf_chip *ci) { u32 regdata; u32 socitype; @@ -708,7 +708,7 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) static void brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) + struct brcmf_chip *ci) { u32 base = ci->c_inf[0].base; @@ -743,15 +743,14 @@ brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, } int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct chip_info **ci_ptr) + struct brcmf_chip **ci_ptr) { int ret; - struct chip_info *ci; + struct brcmf_chip *ci; brcmf_dbg(TRACE, "Enter\n"); - /* alloc chip_info_t */ - ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC); + ci = kzalloc(sizeof(*ci), GFP_ATOMIC); if (!ci) return -ENOMEM; @@ -779,7 +778,7 @@ err: } void -brcmf_sdio_chip_detach(struct chip_info **ci_ptr) +brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr) { brcmf_dbg(TRACE, "Enter\n"); @@ -798,7 +797,7 @@ static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u32 drivestrength) + struct brcmf_chip *ci, u32 drivestrength) { const struct sdiod_drive_str *str_tab = NULL; u32 str_mask; @@ -870,7 +869,7 @@ brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, static void brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) + struct brcmf_chip *ci) { ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0); ci->resetcore(sdiodev, ci, BCMA_CORE_80211, @@ -879,8 +878,8 @@ brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0, 0, 0); } -static bool -brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) +static bool brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci) { u8 core_idx; u32 reg_addr; @@ -903,7 +902,7 @@ brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci) static inline void brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) + struct brcmf_chip *ci) { u8 idx; u32 regdata; @@ -923,9 +922,8 @@ brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); } -static bool -brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, - u32 rstvec) +static bool brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 rstvec) { u8 core_idx; u32 reg_addr; @@ -948,7 +946,7 @@ brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, } void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) + struct brcmf_chip *ci) { u8 arm_core_idx; @@ -962,7 +960,7 @@ void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, } bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u32 rstvec) + struct brcmf_chip *ci, u32 rstvec) { u8 arm_core_idx; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h index a1aadf419536..fb0614329ede 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -54,7 +54,7 @@ #define BRCMF_MAX_CORENUM 6 -struct chip_core_info { +struct brcmf_core { u16 id; u16 rev; u32 base; @@ -63,27 +63,27 @@ struct chip_core_info { u32 cib; }; -struct chip_info { +struct brcmf_chip { u32 chip; u32 chiprev; /* core info */ /* always put chipcommon core at 0, bus core at 1 */ - struct chip_core_info c_inf[BRCMF_MAX_CORENUM]; + struct brcmf_core c_inf[BRCMF_MAX_CORENUM]; u32 pmurev; u32 pmucaps; u32 ramsize; u32 rambase; u32 rst_vec; /* reset vertor for ARM CR4 core */ - bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, + bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci, u16 coreid); - u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, + u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci, u16 coreid); void (*coredisable)(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 pre_resetbits, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, u32 in_resetbits); void (*resetcore)(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 pre_resetbits, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, u32 in_resetbits, u32 post_resetbits); }; @@ -217,14 +217,15 @@ struct sdpcmd_regs { }; int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct chip_info **ci_ptr); -void brcmf_sdio_chip_detach(struct chip_info **ci_ptr); + struct brcmf_chip **ci_ptr); +void brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr); void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u32 drivestrength); -u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid); + struct brcmf_chip *ci, + u32 drivestrength); +u8 brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid); void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci); + struct brcmf_chip *ci); bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u32 rstvec); + struct brcmf_chip *ci, u32 rstvec); #endif /* _BRCMFMAC_SDIO_CHIP_H_ */ From 2668b0b16cb555abe1b35a1c3cdeb482799fb7cb Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:28 +0100 Subject: [PATCH 109/121] brcmfmac: initialize escan function pointer during scheduled scan For scheduled scan results the driver does a escan to obtain BSS details from the firmware. However, the escan uses a dynamically determined function. This needs to be set appropriately when handling scheduled scan results to avoid NULL pointer access. Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index aad83aef7d93..d7718a5fa2f0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2989,6 +2989,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, } set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + cfg->escan_info.run = brcmf_run_escan; err = brcmf_do_escan(cfg, wiphy, ifp, request); if (err) { clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); From bb350711ecb86568468b497d0ecd5c3fd7b45b1b Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:29 +0100 Subject: [PATCH 110/121] brcmfmac: handle SDIO card removal When removing the card the driver still tries to access registers in the device. This patch adds another state for the bus that indicates the device is no longer reachable. This avoids errors accessing it while cleaning up the driver. Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 40 +++--- .../net/wireless/brcm80211/brcmfmac/dhd_bus.h | 21 ++++ .../wireless/brcm80211/brcmfmac/dhd_linux.c | 4 +- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 118 ++++++++---------- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 4 +- 5 files changed, 101 insertions(+), 86 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 34c993dd0602..fa35b23bbaa7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -287,6 +287,9 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, s32 retry = 0; int ret; + if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM) + return -ENOMEDIUM; + /* * figure out how to read the register based on address range * 0x00 ~ 0x7FF: function 0 CCCR and FBR @@ -306,9 +309,12 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, usleep_range(1000, 2000); ret = brcmf_sdiod_request_data(sdiodev, func_num, addr, regsz, data, write); - } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); + } while (ret != 0 && ret != -ENOMEDIUM && + retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); - if (ret != 0) + if (ret == -ENOMEDIUM) + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); + else if (ret != 0) brcmf_err("failed with %d\n", ret); return ret; @@ -320,6 +326,9 @@ brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) int err = 0, i; u8 addr[3]; + if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM) + return -ENOMEDIUM; + addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK; addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK; @@ -429,6 +438,7 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, bool write, u32 addr, struct sk_buff *pkt) { unsigned int req_sz; + int err; brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); if (brcmf_sdiod_pm_resume_error(sdiodev)) @@ -439,18 +449,18 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, req_sz &= (uint)~3; if (write) - return sdio_memcpy_toio(sdiodev->func[fn], addr, - ((u8 *)(pkt->data)), - req_sz); + err = sdio_memcpy_toio(sdiodev->func[fn], addr, + ((u8 *)(pkt->data)), req_sz); else if (fn == 1) - return sdio_memcpy_fromio(sdiodev->func[fn], - ((u8 *)(pkt->data)), - addr, req_sz); + err = sdio_memcpy_fromio(sdiodev->func[fn], ((u8 *)(pkt->data)), + addr, req_sz); else /* function 2 read is FIFO operation */ - return sdio_readsb(sdiodev->func[fn], - ((u8 *)(pkt->data)), addr, - req_sz); + err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr, + req_sz); + if (err == -ENOMEDIUM) + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); + return err; } /** @@ -593,7 +603,11 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req); ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; - if (ret != 0) { + if (ret == -ENOMEDIUM) { + brcmf_bus_change_state(sdiodev->bus_if, + BRCMF_BUS_NOMEDIUM); + break; + } else if (ret != 0) { brcmf_err("CMD53 sg block %s failed %d\n", write ? "write" : "read", ret); ret = -EIO; @@ -852,8 +866,6 @@ int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn) static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) { - sdiodev->bus_if->state = BRCMF_BUS_DOWN; - if (sdiodev->bus) { brcmf_sdio_remove(sdiodev->bus); sdiodev->bus = NULL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 5c12a07673fa..c4535616064e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -17,8 +17,12 @@ #ifndef _BRCMF_BUS_H_ #define _BRCMF_BUS_H_ +#include "dhd_dbg.h" + /* The level of bus communication with the dongle */ enum brcmf_bus_state { + BRCMF_BUS_UNKNOWN, /* Not determined yet */ + BRCMF_BUS_NOMEDIUM, /* No medium access to dongle */ BRCMF_BUS_DOWN, /* Not ready for frame transfers */ BRCMF_BUS_LOAD, /* Download access only (CPU reset) */ BRCMF_BUS_DATA /* Ready for frame transfers */ @@ -144,6 +148,23 @@ struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) return bus->ops->gettxq(bus->dev); } + +static inline bool brcmf_bus_ready(struct brcmf_bus *bus) +{ + return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA; +} + +static inline void brcmf_bus_change_state(struct brcmf_bus *bus, + enum brcmf_bus_state new_state) +{ + /* NOMEDIUM is permanent */ + if (bus->state == BRCMF_BUS_NOMEDIUM) + return; + + brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state); + bus->state = new_state; +} + /* * interface functions from common layer */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index af39edae8c62..d4d966beb840 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -934,7 +934,7 @@ int brcmf_bus_start(struct device *dev) p2p_ifp = NULL; /* signal bus ready */ - bus_if->state = BRCMF_BUS_DATA; + brcmf_bus_change_state(bus_if, BRCMF_BUS_DATA); /* Bus is ready, do any initialization */ ret = brcmf_c_preinit_dcmds(ifp); @@ -1029,6 +1029,8 @@ void brcmf_detach(struct device *dev) /* stop firmware event handling */ brcmf_fweh_detach(drvr); + brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN); + /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) if (drvr->iflist[i]) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ef24a1bd24a7..3e991897d7ca 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -1082,10 +1082,6 @@ static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) /* Clear partial in any case */ bus->cur_read.len = 0; - - /* If we can't reach the device, signal failure */ - if (err) - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } /* return total length of buffer chain */ @@ -1682,8 +1678,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) bus->rxpending = true; for (rd->seq_num = bus->rx_seq, rxleft = maxframes; - !bus->rxskip && rxleft && - bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN; + !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if); rd->seq_num++, rxleft--) { /* Handle glomming separately */ @@ -2232,39 +2227,37 @@ static void brcmf_sdio_bus_stop(struct device *dev) bus->watchdog_tsk = NULL; } - sdio_claim_host(bus->sdiodev->func[1]); + if (bus_if->state == BRCMF_BUS_DOWN) { + sdio_claim_host(sdiodev->func[1]); - /* Enable clock for device interrupts */ - brcmf_sdio_bus_sleep(bus, false, false); + /* Enable clock for device interrupts */ + brcmf_sdio_bus_sleep(bus, false, false); - /* Disable and clear interrupts at the chip level also */ - w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); - local_hostintmask = bus->hostintmask; - bus->hostintmask = 0; + /* Disable and clear interrupts at the chip level also */ + w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); + local_hostintmask = bus->hostintmask; + bus->hostintmask = 0; - /* Change our idea of bus state */ - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; + /* Force backplane clocks to assure F2 interrupt propagates */ + saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if (!err) + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + if (err) + brcmf_err("Failed to force clock for F2: err %d\n", + err); - /* Force clocks on backplane to be sure F2 interrupt propagates */ - saveclk = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (!err) { - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); + /* Turn off the bus (F2), free any pending packets */ + brcmf_dbg(INTR, "disable SDIO interrupts\n"); + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); + + /* Clear any pending interrupts now that F2 is disabled */ + w_sdreg32(bus, local_hostintmask, + offsetof(struct sdpcmd_regs, intstatus)); + + sdio_release_host(sdiodev->func[1]); } - if (err) - brcmf_err("Failed to force clock for F2: err %d\n", err); - - /* Turn off the bus (F2), free any pending packets */ - brcmf_dbg(INTR, "disable SDIO interrupts\n"); - sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); - - /* Clear any pending interrupts now that F2 is disabled */ - w_sdreg32(bus, local_hostintmask, - offsetof(struct sdpcmd_regs, intstatus)); - - sdio_release_host(bus->sdiodev->func[1]); - /* Clear the data packet queues */ brcmu_pktq_flush(&bus->txq, true, NULL, NULL); @@ -2354,20 +2347,11 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) /* Check for inconsistent device control */ devctl = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_DEVICE_CTL, &err); - if (err) { - brcmf_err("error reading DEVCTL: %d\n", err); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - } #endif /* DEBUG */ /* Read CSR, if clock on switch to AVAIL, else ignore */ clkctl = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (err) { - brcmf_err("error reading CSR: %d\n", - err); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - } brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl); @@ -2375,19 +2359,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) if (SBSDIO_HTAV(clkctl)) { devctl = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_DEVICE_CTL, &err); - if (err) { - brcmf_err("error reading DEVCTL: %d\n", - err); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - } devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, devctl, &err); - if (err) { - brcmf_err("error writing DEVCTL: %d\n", - err); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - } bus->clkstate = CLK_AVAIL; } } @@ -2522,9 +2496,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) txlimit -= framecnt; } - if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) || (err != 0)) { + if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { brcmf_err("failed backplane access over SDIO, halting operation\n"); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; atomic_set(&bus->intstatus, 0); } else if (atomic_read(&bus->intstatus) || atomic_read(&bus->ipend) > 0 || @@ -3356,7 +3329,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) } /* Allow HT Clock now that the ARM is running. */ - bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD; + brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD); bcmerror = 0; err: @@ -3633,7 +3606,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) return; } - if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { + if (!brcmf_bus_ready(bus->sdiodev->bus_if)) { brcmf_err("bus is down. we have nothing to do\n"); return; } @@ -3644,7 +3617,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) else if (brcmf_sdio_intr_rstatus(bus)) { brcmf_err("failed backplane access\n"); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } /* Disable additional interrupts (is this needed now)? */ @@ -3781,6 +3753,11 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) goto fail; } + /* SDIO register access works so moving + * state from UNKNOWN to DOWN. + */ + brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); + if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci)) { brcmf_err("brcmf_sdio_chip_attach failed!\n"); goto fail; @@ -4004,7 +3981,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) /* Disable F2 to clear any intermediate frame state on the dongle */ sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; bus->rxflow = false; /* Done with backplane-dependent accesses, can drop clock... */ @@ -4060,16 +4036,20 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) } if (bus->ci) { - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - /* Leave the device in state where it is 'quiet'. This - * is done by putting it in download_state which - * essentially resets all necessary cores - */ - msleep(20); - brcmf_sdio_chip_enter_download(bus->sdiodev, bus->ci); - brcmf_sdio_clkctl(bus, CLK_NONE, false); - sdio_release_host(bus->sdiodev->func[1]); + if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + /* Leave the device in state where it is + * 'quiet'. This is done by putting it in + * download_state which essentially resets + * all necessary cores. + */ + msleep(20); + brcmf_sdio_chip_enter_download(bus->sdiodev, + bus->ci); + brcmf_sdio_clkctl(bus, CLK_NONE, false); + sdio_release_host(bus->sdiodev->func[1]); + } brcmf_sdio_chip_detach(&bus->ci); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index c345c32eb631..24f65cd53859 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -522,10 +522,10 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) /* update state of upper layer */ if (state == BRCMFMAC_USB_STATE_DOWN) { brcmf_dbg(USB, "DBUS is down\n"); - bcmf_bus->state = BRCMF_BUS_DOWN; + brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN); } else if (state == BRCMFMAC_USB_STATE_UP) { brcmf_dbg(USB, "DBUS is up\n"); - bcmf_bus->state = BRCMF_BUS_DATA; + brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DATA); } else { brcmf_dbg(USB, "DBUS current state=%d\n", state); } From 1908861f2fb072446eabbdc4a01c3bec40ed2f7c Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 14 Jan 2014 09:22:36 +0530 Subject: [PATCH 111/121] ath9k: Fix error reported by smatch debug_sta.c: ath_debug_rate_stats() error: buffer overflow 'rstats->ht_stats' 24 <= 24 Cc: Dan Carpenter Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug_sta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index d33cc88a5849..d76e6e0120d2 100644 --- a/drivers/net/wireless/ath/ath9k/debug_sta.c +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -128,7 +128,7 @@ void ath_debug_rate_stats(struct ath_softc *sc, rxs = IEEE80211_SKB_RXCB(skb); if (IS_HT_RATE(rs->rs_rate)) { - if (rxs->rate_idx > ARRAY_SIZE(rstats->ht_stats)) + if (rxs->rate_idx >= ARRAY_SIZE(rstats->ht_stats)) goto exit; if (rxs->flag & RX_FLAG_40MHZ) From 522aaa182a8d942d29e37e1f80b3f5b3c68cc475 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 14 Jan 2014 13:25:17 +0530 Subject: [PATCH 112/121] ath9k: Fix IQ calibration This patch fixes a bug in the TX IQ calibration post processing routine because of which the driver disables TX IQ correction even though the calibration results are valid. This fix is applicable for all chips in the AR9003 family. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 8c145cd98c1c..4eb35aa64f35 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -655,8 +655,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, if (i2_m_q2_a0_d1 > 0x800) i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1); - if (i2_p_q2_a0_d1 > 0x800) - i2_p_q2_a0_d1 = -((0xfff - i2_p_q2_a0_d1) + 1); + if (i2_p_q2_a0_d1 > 0x1000) + i2_p_q2_a0_d1 = -((0x1fff - i2_p_q2_a0_d1) + 1); if (iq_corr_a0_d1 > 0x800) iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1); @@ -700,6 +700,19 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, return false; } + if ((i2_p_q2_a0_d0 < 1024) || (i2_p_q2_a0_d0 > 2047) || + (i2_p_q2_a1_d0 < 0) || (i2_p_q2_a1_d1 < 0) || + (i2_p_q2_a0_d0 <= i2_m_q2_a0_d0) || + (i2_p_q2_a0_d0 <= iq_corr_a0_d0) || + (i2_p_q2_a0_d1 <= i2_m_q2_a0_d1) || + (i2_p_q2_a0_d1 <= iq_corr_a0_d1) || + (i2_p_q2_a1_d0 <= i2_m_q2_a1_d0) || + (i2_p_q2_a1_d0 <= iq_corr_a1_d0) || + (i2_p_q2_a1_d1 <= i2_m_q2_a1_d1) || + (i2_p_q2_a1_d1 <= iq_corr_a1_d1)) { + return false; + } + mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0; phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0; From 3b24e9f8c07fc09174e4f2fe4d0f716595ae2146 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 14 Jan 2014 13:41:17 +0530 Subject: [PATCH 113/121] ath9k: Fix possible overflow condition Prevent a possible overflow condition which results in occasional bad IQ coefficients and EVM numbers. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 4eb35aa64f35..a352128c40ad 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -565,7 +565,7 @@ static bool ar9003_hw_solve_iq_cal(struct ath_hw *ah, const s32 result_shift = 1 << 15; struct ath_common *common = ath9k_hw_common(ah); - f2 = (f1 * f1 + f3 * f3) / result_shift; + f2 = ((f1 >> 3) * (f1 >> 3) + (f3 >> 3) * (f3 >> 3)) >> 9; if (!f2) { ath_dbg(common, CALIBRATE, "Divide by 0\n"); From 0786dc4edd2d89053d6366c4fbc6b458ee2e9b5e Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Tue, 14 Jan 2014 20:31:26 +0200 Subject: [PATCH 114/121] wil6210: correct #include for prefetch() This fixes bug found by the kbuild test robot: tree: git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next.git master head: 1e2f9295f4c657500111514f92a3d3894d0e05b4 commit: 1cbbcb08c786964a16773c39f2536f1923c73c58 [135/140] wil6210: prefetch head of packet config: make ARCH=microblaze allyesconfig All error/warnings: drivers/net/wireless/ath/wil6210/txrx.c: In function 'wil_vring_reap_rx': >> drivers/net/wireless/ath/wil6210/txrx.c:381:2: error: implicit declaration of function 'prefetch' [-Werror=implicit-function-declaration] prefetch(skb->data); ^ cc1: some warnings being treated as errors vim +/prefetch +381 drivers/net/wireless/ath/wil6210/txrx.c 375 wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); 376 kfree_skb(skb); 377 return NULL; 378 } 379 skb_trim(skb, dmalen); 380 > 381 prefetch(skb->data); 382 383 wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, 384 skb->data, skb_headlen(skb), false); Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 9b88440ef05b..0b0975d88b43 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "wil6210.h" #include "wmi.h" From 1e202242ee1432d68a8bea4919b2ae0ef19d9e06 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Tue, 14 Jan 2014 19:16:34 -0800 Subject: [PATCH 115/121] mwifiex: fix wrong 11ac bits setting in fw_cap_info bit 14 is actually reserved and bit 12 & 13 should be used for 11ac capability in fw_cap_info. Cc: # 3.9+ Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/fw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 4cee6ceb7e9e..5fa932d5f905 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -226,7 +226,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { /* HW_SPEC fw_cap_info */ -#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & (BIT(13)|BIT(14))) +#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & (BIT(12)|BIT(13))) #define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3) #define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3) From f862bfd1837c36efbb7f3475dbd409ce86edb080 Mon Sep 17 00:00:00 2001 From: Ujjal Roy Date: Tue, 14 Jan 2014 19:22:22 -0800 Subject: [PATCH 116/121] mwifiex: export threshold value and retry limit to cfg80211 While registering the driver with cfg80211, update the threshold value and retry limit to cfg80211. Signed-off-by: Ujjal Roy Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e7c81abf108e..f37b403e83d0 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2677,6 +2677,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) struct wiphy *wiphy; struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; u8 *country_code; + u32 thr, retry; /* create a new wiphy for use with cfg80211 */ wiphy = wiphy_new(&mwifiex_cfg80211_ops, @@ -2766,6 +2767,19 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) country_code); } + mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr); + wiphy->frag_threshold = thr; + mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr); + wiphy->rts_threshold = thr; + mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry); + wiphy->retry_short = (u8) retry; + mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry); + wiphy->retry_long = (u8) retry; + adapter->wiphy = wiphy; return ret; } From f1d267cab2bc6ecd225abfd8d7e130f9a651408f Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:37:42 +0100 Subject: [PATCH 117/121] ath: add common function ath_is_mybeacon this function is used by most ath driver, so it can be moved here. Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 2 ++ drivers/net/wireless/ath/main.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index e0ba7cd14252..b59cfbe0276b 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -17,6 +17,7 @@ #ifndef ATH_H #define ATH_H +#include #include #include #include @@ -165,6 +166,7 @@ struct ath_common { struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, u32 len, gfp_t gfp_mask); +bool ath_is_mybeacon(struct ath_common *common, struct ieee80211_hdr *hdr); void ath_hw_setbssidmask(struct ath_common *common); void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key); diff --git a/drivers/net/wireless/ath/main.c b/drivers/net/wireless/ath/main.c index 8e99540cd90e..8b0ac14d5c32 100644 --- a/drivers/net/wireless/ath/main.c +++ b/drivers/net/wireless/ath/main.c @@ -59,6 +59,14 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, } EXPORT_SYMBOL(ath_rxbuf_alloc); +bool ath_is_mybeacon(struct ath_common *common, struct ieee80211_hdr *hdr) +{ + return ieee80211_is_beacon(hdr->frame_control) && + !is_zero_ether_addr(common->curbssid) && + ether_addr_equal_64bits(hdr->addr3, common->curbssid); +} +EXPORT_SYMBOL(ath_is_mybeacon); + void ath_printk(const char *level, const struct ath_common* common, const char *fmt, ...) { From 1cc47a5b2417b319d022d8aa995888d791b1eee1 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:07:15 +0100 Subject: [PATCH 118/121] ath9k: use ath_is_mybeacon This patch will also change behavior of rx_beacons statistic. Instead of collecting all received beacons, it will collect only ours. This, IMO make more sense, since for troubleshooting we will need to know count of our beacons, or both. Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 5bf3243dce0f..a0ebdd000fc2 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -969,21 +969,6 @@ static void ath9k_process_tsf(struct ath_rx_status *rs, rxs->mactime += 0x100000000ULL; } -static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - - if (ieee80211_is_beacon(hdr->frame_control)) { - RX_STAT_INC(rx_beacons); - if (!is_zero_ether_addr(common->curbssid) && - ether_addr_equal_64bits(hdr->addr3, common->curbssid)) - return true; - } - - return false; -} - /* * For Decrypt or Demic errors, we only mark packet status here and always push * up the frame up to let mac80211 handle the actual error case, be it no @@ -1071,7 +1056,10 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, goto exit; } - rx_stats->is_mybeacon = ath9k_is_mybeacon(sc, hdr); + if (ath_is_mybeacon(common, hdr)) { + RX_STAT_INC(rx_beacons); + rx_stats->is_mybeacon = true; + } /* * This shouldn't happen, but have a safety check anyway. From aeb0e356ec6e0d11b8369bbbb32ae81b92a1a46b Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:07:16 +0100 Subject: [PATCH 119/121] ath9k_htc: use ath_is_mybeacon Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index b41e008298dc..12e0f32a4905 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -1075,9 +1075,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, last_rssi = priv->rx.last_rssi; - if (ieee80211_is_beacon(hdr->frame_control) && - !is_zero_ether_addr(common->curbssid) && - ether_addr_equal_64bits(hdr->addr3, common->curbssid)) { + if (ath_is_mybeacon(common, hdr)) { s8 rssi = rxbuf->rxstatus.rs_rssi; if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) From d44efe21859777784aea9afef934ad67bbb16935 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:11:15 +0100 Subject: [PATCH 120/121] ath5k: use ath_is_mybeacon Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 33 +++++++-------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index d85c312170bc..ef35da84f63b 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1238,14 +1238,11 @@ static void ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb, struct ieee80211_rx_status *rxs) { - struct ath_common *common = ath5k_hw_common(ah); u64 tsf, bc_tstamp; u32 hw_tu; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - if (ieee80211_is_beacon(mgmt->frame_control) && - le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && - ether_addr_equal_64bits(mgmt->bssid, common->curbssid)) { + if (le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS) { /* * Received an IBSS beacon with the same BSSID. Hardware *must* * have updated the local TSF. We have to work around various @@ -1301,23 +1298,6 @@ ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb, } } -static void -ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi) -{ - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - struct ath_common *common = ath5k_hw_common(ah); - - /* only beacons from our BSSID */ - if (!ieee80211_is_beacon(mgmt->frame_control) || - !ether_addr_equal_64bits(mgmt->bssid, common->curbssid)) - return; - - ewma_add(&ah->ah_beacon_rssi_avg, rssi); - - /* in IBSS mode we should keep RSSI statistics per neighbour */ - /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */ -} - /* * Compute padding position. skb must contain an IEEE 802.11 frame */ @@ -1390,6 +1370,7 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, struct ath5k_rx_status *rs) { struct ieee80211_rx_status *rxs; + struct ath_common *common = ath5k_hw_common(ah); ath5k_remove_padding(skb); @@ -1442,11 +1423,13 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, trace_ath5k_rx(ah, skb); - ath5k_update_beacon_rssi(ah, skb, rs->rs_rssi); + if (ath_is_mybeacon(common, (struct ieee80211_hdr *)skb->data)) { + ewma_add(&ah->ah_beacon_rssi_avg, rs->rs_rssi); - /* check beacons in IBSS mode */ - if (ah->opmode == NL80211_IFTYPE_ADHOC) - ath5k_check_ibss_tsf(ah, skb, rxs); + /* check beacons in IBSS mode */ + if (ah->opmode == NL80211_IFTYPE_ADHOC) + ath5k_check_ibss_tsf(ah, skb, rxs); + } ieee80211_rx(ah->hw, skb); } From e4e19c031901e95dc7d1cf0a2c9c50525d71651f Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:11:39 +0100 Subject: [PATCH 121/121] carl9170: use ath_is_mybeacon Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/rx.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 1b1b20751ead..536bc46a2912 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -519,6 +519,7 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) { struct ieee80211_hdr *hdr = data; struct ieee80211_tim_ie *tim_ie; + struct ath_common *common = &ar->common; u8 *tim; u8 tim_len; bool cam; @@ -526,17 +527,13 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) if (likely(!(ar->hw->conf.flags & IEEE80211_CONF_PS))) return; - /* check if this really is a beacon */ - if (!ieee80211_is_beacon(hdr->frame_control)) - return; - /* min. beacon length + FCS_LEN */ if (len <= 40 + FCS_LEN) return; + /* check if this really is a beacon */ /* and only beacons from the associated BSSID, please */ - if (!ether_addr_equal_64bits(hdr->addr3, ar->common.curbssid) || - !ar->common.curaid) + if (!ath_is_mybeacon(common, hdr) || !common->curaid) return; ar->ps.last_beacon = jiffies;