From 2b6a6a90c31c550f839e905484f5253b9d24e33d Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 5 Dec 2013 06:53:41 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 1260a8d..9ea333a 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 & -- cgit v0.10.2 From 2256940010aab059dc238ab81b7b562b17942ec8 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 13 Dec 2013 13:44:16 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 7fc7919..b93ae35 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 fcb534f..fe8bd1b 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 2282980..74f45fa6 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) -- cgit v0.10.2 From 453cdb61c015f80e492dda217110353cd09eef18 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 13 Dec 2013 10:44:43 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 9ea333a..3ec6c9a 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); -- cgit v0.10.2 From f259509baafcf180c6146bc5bcc0ae7688e5017a Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 10 Dec 2013 16:20:39 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ce9ef349..52f6187 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; -- cgit v0.10.2 From d354181f4d9a4b91ddbc90327cba6692ef3779a1 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 10 Dec 2013 16:20:40 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 79726e0..035cbf6 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 52f6187..5b45f3a 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 | -- cgit v0.10.2 From 194ff52d42fd0b55fe0fcfbf4586ae9d7ab1f780 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 30 Dec 2013 23:12:37 +0100 Subject: 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 diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 09d2e58..fd1020e 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 5618888..329b0ef 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: -- cgit v0.10.2 From fe0219cc505578636a6ed38d493e41ea5d599e3e Mon Sep 17 00:00:00 2001 From: Salil Kapur Date: Thu, 31 Oct 2013 23:17:46 +0530 Subject: 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 diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 1d78605..11c7cbd 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; } -- cgit v0.10.2 From 34f7e57b231ec4fd943d0d172fb7c1bb143f1da8 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Thu, 14 Nov 2013 17:07:32 -0800 Subject: 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 diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 51e21a8..3df4a10 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, 0x84}, 0x13}, - {{0x99, 0x81}, 0x7f}, - {{0x99, 0x31}, 0x70}, + {{0x9b, 0xd1}, 0x17}, + {{0x9b, 0xd2}, 0x58}, + {{0x9b, 0xd3}, 0x10}, + {{0x9b, 0xd4}, 0x47}, + {{0x9b, 0xd5}, 0x0c}, + {{0x9b, 0xd6}, 0x37}, + {{0x9b, 0xdd}, 0x33}, + + {{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); -- cgit v0.10.2 From 249eb5bd74553da189fbff90c20c6d884a1db71e Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 13 Nov 2013 01:00:07 +0100 Subject: 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 diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 3b96100..d45b638 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) -- cgit v0.10.2 From 43d53c29dd8548404256c05573ff557c927d214b Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Sat, 30 Nov 2013 16:14:57 +0100 Subject: 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 diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 693cd1a..80057a8 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); -- cgit v0.10.2 From 11bfb1c4b94fe24a83ebeae5c3310280f9606e0e Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Sat, 30 Nov 2013 16:59:23 +0100 Subject: 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 diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 80057a8..bec6ed1 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 1349074..6184bd1 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 69fbc8d..4a53bb5 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; -- cgit v0.10.2 From a434c2407467a76c0e1416c45f7b31cfbe1b6b3b Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sun, 22 Dec 2013 01:00:20 +0100 Subject: 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 diff --git a/net/nfc/core.c b/net/nfc/core.c index 02ab341..c1903f4 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); -- cgit v0.10.2 From ea87a5efa9efa84cd48fbf7a969d951b32c9e5e4 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sat, 4 Jan 2014 03:23:35 +0100 Subject: 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 diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 3df19e6..cf1a87b 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; -- cgit v0.10.2 From 444fb98eed98f7292a83f9bf123d1c78f171327e Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:12 +0100 Subject: 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 diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 36acecd..81af21e 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, -- cgit v0.10.2 From 67af1d7a0fb4b5397cb093c0fe98d5b95516c228 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:13 +0100 Subject: 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 diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 07bbc24..e42ecb6 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); -- cgit v0.10.2 From 4f319e3251937a73719b13397d738640622ba513 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:14 +0100 Subject: 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 diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index e42ecb6..470a0b4 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; -- cgit v0.10.2 From c36aeba8c072d20a08e8546483494ab0f6458963 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 5 Jan 2014 00:08:05 +0400 Subject: 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 diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 8a0571e..a8555f8 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); -- cgit v0.10.2 From fa9be5f009e8cfc695a2cd71617fc620ab5c1c18 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 23 Dec 2013 14:15:13 -0800 Subject: 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 diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f0e955e..0a2ee83 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); -- cgit v0.10.2 From 1b000789a4fe5f2013cc120a97d8c9b9c57b5431 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 19 Dec 2013 10:47:48 +0100 Subject: 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 diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4576ba0..8da3af2 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 da93666..a0b0aea 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) -- cgit v0.10.2 From e03ad6eade141daf0df07f1c312e8ae702327939 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jan 2014 17:22:30 +0100 Subject: 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 diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b4f40fe..2085712 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) -- cgit v0.10.2 From 4cd3c4ecfcbf07fe58ce7de4de61cf85ddc10e78 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 15:47:15 +0100 Subject: 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 diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 04b5a14..b62cbe8 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 */ -- cgit v0.10.2 From ef04a29737dd08352fdf6431d119ca636d664efe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 15:56:59 +0100 Subject: 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 diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8da3af2..703081d 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) -- cgit v0.10.2 From 5b0ec94f9cfdcd10f6eb4df75fba9404c72fea4d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 16:02:34 +0100 Subject: 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 diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2bd5b55..d767cfb 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); -- cgit v0.10.2 From 6b5895d93d460105a384775bc675221ef377d40a Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Fri, 20 Dec 2013 16:39:52 +0800 Subject: 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 diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index cf83217..e8f60aa 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; } -- cgit v0.10.2 From 60a4fe0ae97b515f174f9bcb157cbaeff0dab8ac Mon Sep 17 00:00:00 2001 From: Ujjal Roy Date: Mon, 6 Jan 2014 21:56:11 +0530 Subject: 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 diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index e7c6e86..051d961 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; -- cgit v0.10.2 From 057d5f4ba1e421185a8e7e0b7fadf253d41a3e83 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 19 Dec 2013 10:25:15 -0800 Subject: 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 diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fd1020e..f9ae9b8 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 b62cbe8..ebf80f3 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 953b9e2..3701930 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 5a74b24..5b919ca 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 df00f19..676dc09 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; +} -- cgit v0.10.2 From 79012f8bed417847ec028bd67547eae4eb832205 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 21:53:27 +0100 Subject: mac80211_hwsim: fix a print message The prefix should be mac80211_hwsim, not mac_80211_hwsim. Signed-off-by: Johannes Berg diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 9c0cc8d..9e7d7df 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: -- cgit v0.10.2 From 1907299867431fd899ae630a29b08b878ca1e50f Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:15 -0800 Subject: 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 diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 0a2ee83..6fbb7ba 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)); -- cgit v0.10.2 From 86e8586ed5beea15ce7c359f02a1084c2da93bc7 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:17 -0800 Subject: 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 diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 6126f1f..5c6fadd 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 6fbb7ba..29c1caf 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)); -- cgit v0.10.2 From 22c15bf30b70ab2eae300f093ffc64e182620aba Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:18 -0800 Subject: 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 diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 5c6fadd..2b93b77 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 29c1caf..46bda01 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); -- cgit v0.10.2 From f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:19 -0800 Subject: 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 diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index c1fb206..fe20e1c 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 c715fe8..56ab822 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 0000000..5e18afd --- /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 0000000..97a0de7 --- /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 0000000..396fc27 --- /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 0000000..a007175 --- /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 0000000..3221ca3 --- /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"); -- cgit v0.10.2 From 15203b4c79aaeb443f89ac3190eed3447bf2178b Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:20 -0800 Subject: 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 diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 396fc27..5f91d45 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 a007175..54c4a95 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 { -- cgit v0.10.2 From 87ee475ef62504609368a767f6ad2e903a442078 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 7 Jan 2014 13:09:38 +0200 Subject: mac80211: clean up garbage in comment Not clear how this landed here. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 7313d37..21448d6 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; } -- cgit v0.10.2 From 40791942ec6befaad04073ed97adb8ab04ab43f3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 7 Jan 2014 13:10:20 +0200 Subject: 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 diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5a2afe9..8cb20d7 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) { -- cgit v0.10.2 From 3c2723f503e8decc420b377a3215089a7980cf08 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jan 2014 16:23:24 +0100 Subject: 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 diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 8cb20d7..c24ca0d 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; } /* -- cgit v0.10.2 From 349b196044d38a3bf5be2b4dd2fe28ca2f12a258 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 7 Jan 2014 13:11:41 +0200 Subject: 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 diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 9a8be8f..fab7b91 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; -- cgit v0.10.2 From 48e1044515967a0d88ee076045b2141535557d8e Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 6 Jan 2014 23:34:37 +0100 Subject: 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 diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 09fc954..c129d15 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; + + rc = digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len); - return 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 470a0b4..562bec9 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"); -- cgit v0.10.2 From b711ad524bf5a6a078c4d0a1a44ca1db204802f6 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 6 Jan 2014 23:34:48 +0100 Subject: 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 diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 562bec9..43e450f 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); -- cgit v0.10.2 From 685328b296acc810541d2532957912690273c64a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:24:00 +0100 Subject: 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 diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index cfce83e..54afde0 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 34c8a33..8cfd1d6 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 ce9ef349..747c8bd 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 69f58b0..fe6878b 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 c3676bf..bcdb50a 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 609b7e1..d878769 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 349fa22..5ab68fd 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 e71ce8c..925034b 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 090f015..1be819d 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 9e7d7df..59c65c3 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 b953ad6..721c738 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 067e6f2..b61fefa 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 fcf9b62..2cfb6d4 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 3291ffa..ee4ec4e 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 e9da47c..18a009e 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 07891a3..0d29624 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 f838af8..25b1887 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; -- cgit v0.10.2 From bb55dc2ae4367b8f711d43a2f8668a6ed42c4fd3 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 8 Jan 2014 10:52:27 -0800 Subject: 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 diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 5f91d45..85e8bcf 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); -- cgit v0.10.2 From bdfbec2d2d240e9c528caae9c743801629b60166 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 9 Jan 2014 11:37:23 +0200 Subject: 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 diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 116f4ab..32f7500 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 56c5977..b1f84b0 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 2085712..d0afd82 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 a32d52a..b528e31 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 3f64202..c854f1c 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 329b0ef..d39c371 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) = -- cgit v0.10.2 From f5aa0d21dd5ada040ff42b4d40357285e4ace441 Mon Sep 17 00:00:00 2001 From: Ujjal Roy Date: Thu, 9 Jan 2014 21:16:14 +0530 Subject: 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 diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 051d961..5661a54 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; -- cgit v0.10.2 From 852c0153df5082311f50c062275813905f39f56e Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Thu, 9 Jan 2014 15:22:57 -0800 Subject: 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 diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 776cbb8..b382c7c 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 -- cgit v0.10.2 From 0a1cb80975b67e29d572b28c1621203d1d74f4d3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 9 Jan 2014 11:05:31 +0100 Subject: 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 diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 703081d..93bfd67 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; } } -- cgit v0.10.2 From f9f760b4883d7fbfb463a67267e2be6b440d1aeb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Jan 2014 17:45:07 +0100 Subject: 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 diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 93bfd67..93e2157 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; - - 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 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; - /* - * 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. */ } -- cgit v0.10.2 From 03c8c06f2d080b841ecbfc63253228ba6efcab08 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 9 Jan 2014 01:45:28 +0100 Subject: 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 diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 377cf97..9f3cbf1 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); -- cgit v0.10.2 From b77cf4f8e1892e192ec52df5dd8c158b300fc496 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 9 Jan 2014 00:00:38 +0100 Subject: 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 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 25b1887..f4ab2fb 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 93e2157..decd30c 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; } - if (qoshdr) - tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); - else - tids |= BIT(0); + /* 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; - __skb_queue_tail(&pending, skb); + 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++; + } + 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 { /* -- cgit v0.10.2 From baccfe6e86443c8b7ba7f21144eedfb100ecf816 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 23 Dec 2013 08:38:27 +0200 Subject: ath10k: add DFS_CERTIFIED option Add Kconfig option that allow DFS functionality. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 82e8088..a6f5285 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. -- cgit v0.10.2 From cdf074099babdda9dccae7d421bd5566d70e75e3 Mon Sep 17 00:00:00 2001 From: Marek Puzyniak Date: Mon, 30 Dec 2013 09:07:51 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 5b45f3a..2a4e7aa 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) { -- cgit v0.10.2 From 0e759f363e9f2f1b1a7a640b15ca613e5e27dfc0 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 2 Jan 2014 14:38:33 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 035cbf6..9d8fc29 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 9535eaa..f1505a2 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 2a4e7aa..ce0f1db 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; } -- cgit v0.10.2 From 2fe5288c11dae71c178a9d948347933b0b76a5f5 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 3 Jan 2014 12:59:20 +0200 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 3ec6c9a..f992570 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) -- cgit v0.10.2 From 869526b958f59592c46086c11f638824b08d164a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 3 Jan 2014 12:59:26 +0200 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index 90817dd..4eb2ecb 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 f992570..4e043bb 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, -- cgit v0.10.2 From f118a3e515fd713a0ade315018f6b645bc98916e Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 3 Jan 2014 12:59:31 +0200 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 9d8fc29..3721c43 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 6bdfad3..6debd28 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 4e043bb..712a606 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 0087d69..4aa1489 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_ */ -- cgit v0.10.2 From 51ab1a0a09a894442e8b091d57222c5425b6d700 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 8 Jan 2014 09:08:33 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 3721c43..ade1781 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 ce0f1db..7aa6c4d 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 4aa1489..4b5e7d3 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, -- cgit v0.10.2 From 28cb1749e8dcf7f2b49174ae8531e3e5be741736 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 10 Jan 2014 11:08:55 -0800 Subject: 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 diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b382c7c..e526a8c 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 -- cgit v0.10.2 From cf7a6b2d5bd73acd8e3a78cf03259489bd83498a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 21:55:14 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 59c65c3..03d7b1d 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[] = { -- cgit v0.10.2 From 38ed5048fe84d6048835916f298eaa623f59ace4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:11:06 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 03d7b1d..4e94224 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); -- cgit v0.10.2 From 3a8cc5e73f7105faf32fb8df57ba3e7b967e1982 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:26:23 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4e94224..55cdfd6 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); -- cgit v0.10.2 From f39c2bfa9a1e2bae726cce65d2d328652e81f0c2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:39:27 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 55cdfd6..23fa6ee 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; } -- cgit v0.10.2 From e4afb603c0799fbca3351320180ec3c8fbeefa6d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:43:11 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 23fa6ee..cca5b3f 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_free(void) +static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data) { - struct list_head tmplist, *i, *tmp; - struct mac80211_hwsim_data *data, *tmpdata; + debugfs_remove_recursive(data->debugfs); + ieee80211_unregister_hw(data->hw); + device_release_driver(data->dev); + device_unregister(data->dev); + ieee80211_free_hw(data->hw); +} - INIT_LIST_HEAD(&tmplist); +static void mac80211_hwsim_free(void) +{ + 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); } -- cgit v0.10.2 From 8133841045b81639495dc5ca1bcab45d279661ab Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:53:42 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index cca5b3f..59caa10 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)); -- cgit v0.10.2 From 2d68992b6079cd5bdfff85251c3c670ce1560d5c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:56:30 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 59caa10..8df3d5e 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; } -- cgit v0.10.2 From 9ddd12af103f60fc284103740c07219d888d1aee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 23:03:02 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 8df3d5e..ca156a5 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 */ -- cgit v0.10.2 From de0421d53bfb5f7783e5228f8cf49f2ae7140d54 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 23:12:12 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index ca156a5..1c51c33 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,210 +1818,265 @@ 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) +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); - 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); - } + idx = hwsim_radio_idx++; spin_unlock_bh(&hwsim_radio_lock); - 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, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; + 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; -static void hwsim_mon_setup(struct net_device *dev) -{ - dev->netdev_ops = &hwsim_netdev_ops; - dev->destructor = free_netdev; - ether_setup(dev); - dev->tx_queue_len = 0; - dev->type = ARPHRD_IEEE80211_RADIOTAP; - memset(dev->dev_addr, 0, ETH_ALEN); - dev->dev_addr[0] = 0x12; -} + 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); -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; + 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; - if (!vp->assoc) - return; + data->channels = channels; - wiphy_debug(data->hw->wiphy, - "%s: send PS-Poll to %pM for aid %d\n", - __func__, vp->bssid, vp->aid); + 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); + } - 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); + INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); + INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); - rcu_read_lock(); - mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); - rcu_read_unlock(); -} + 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); -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; + 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; - if (!vp->assoc) - return; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_AP_UAPSD; + hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; - wiphy_debug(data->hw->wiphy, - "%s: send data::nullfunc to %pM ps=%d\n", - __func__, vp->bssid, ps); + /* 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); - 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); + 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)); - rcu_read_lock(); - mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); - rcu_read_unlock(); -} + 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; -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); -} + 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; + } -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); -} + /* 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; -static int hwsim_fops_ps_read(void *dat, u64 *val) -{ - struct mac80211_hwsim_data *data = dat; - *val = data->ps; - return 0; -} + err = ieee80211_register_hw(hw); + if (err < 0) { + printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", + err); + goto failed_hw; + } -static int hwsim_fops_ps_write(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - enum ps_mode old_ps; + wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); - if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && - val != PS_MANUAL_POLL) - return -EINVAL; + 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); - old_ps = data->ps; - data->ps = val; + tasklet_hrtimer_init(&data->beacon_timer, + mac80211_hwsim_beacon, + CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); - 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); - } + spin_lock_bh(&hwsim_radio_lock); + list_add_tail(&data->list, &hwsim_radios); + spin_unlock_bh(&hwsim_radio_lock); return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, - "%llu\n"); +failed_hw: + device_unregister(data->dev); +failed_drvdata: + ieee80211_free_hw(hw); +failed: + return err; +} -static int hwsim_write_simulate_radar(void *dat, u64 val) +static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data) { - struct mac80211_hwsim_data *data = dat; - - ieee80211_radar_detected(data->hw); - - return 0; + debugfs_remove_recursive(data->debugfs); + ieee80211_unregister_hw(data->hw); + device_release_driver(data->dev); + device_unregister(data->dev); + ieee80211_free_hw(data->hw); } -DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL, - hwsim_write_simulate_radar, "%llu\n"); - -static int hwsim_fops_group_read(void *dat, u64 *val) +static void mac80211_hwsim_free(void) { - struct mac80211_hwsim_data *data = dat; - *val = data->group; - return 0; + struct mac80211_hwsim_data *data; + + spin_lock_bh(&hwsim_radio_lock); + 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); } -static int hwsim_fops_group_write(void *dat, u64 val) +static const struct net_device_ops hwsim_netdev_ops = { + .ndo_start_xmit = hwsim_mon_xmit, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static void hwsim_mon_setup(struct net_device *dev) { - struct mac80211_hwsim_data *data = dat; - data->group = val; - return 0; + dev->netdev_ops = &hwsim_netdev_ops; + dev->destructor = free_netdev; + ether_setup(dev); + dev->tx_queue_len = 0; + dev->type = ARPHRD_IEEE80211_RADIOTAP; + memset(dev->dev_addr, 0, ETH_ALEN); + dev->dev_addr[0] = 0x12; } -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; -- cgit v0.10.2 From 85ca8fc74671def6041fb12c0a7d3423da56ffd3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jan 2014 23:21:34 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 1c51c33..4ee88a9 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, " -- cgit v0.10.2 From c5ac08548b53c3292aa3cbd02de6ae99ca7271e6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jan 2014 23:28:08 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4ee88a9..5bad3d4 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 */ -- cgit v0.10.2 From bc7910989601fbde02b3dd485d595a614d21ef1e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 23:29:20 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 5bad3d4..056dae7 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 afaad5a..fb9920a 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) -- cgit v0.10.2 From 26b0e411d37a2ca5992d02884dc3fa4e1907e598 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jan 2014 13:37:38 +0100 Subject: 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 diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 056dae7..dc7f72e 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 fb9920a..2747cce 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) -- cgit v0.10.2 From 5691e218a446753a2d21da231651b04e58c76b75 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 31 Dec 2013 21:05:50 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 14a974e..2ac3b25 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); -- cgit v0.10.2 From a11e144e41f7979608c8bfd35b297b8162fcf32d Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 31 Dec 2013 21:19:55 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 2ac3b25..208771a 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); } -- cgit v0.10.2 From 91b0d1198417cf4fd9a7bd4138b6909f0b359099 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 5 Jan 2014 12:41:12 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 552c76a..2437a06 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); -- cgit v0.10.2 From 75d3e2d693a695d2a9df27de697fb73cb3f34c23 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 7 Jan 2014 18:19:35 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index c9ff41c..54887b9 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: -- cgit v0.10.2 From 2775613f4ff32f8be2cae42d542b7fbefcafbfbe Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 2 Jan 2014 10:01:46 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 3c575a3..a4d3bce 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); -- cgit v0.10.2 From 8fc685803a8d7b42a5f69dcad7f7cef6f8b60edf Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 14 Dec 2013 20:09:05 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 9e15473..56c3d05 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; -- cgit v0.10.2 From 4f7b91404cd5da3657a82b00394f4f5dfbff13d6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 14 Dec 2013 20:09:06 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 56c3d05..e5e9059 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 c96a0b8..b07cdc9 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 7d20d84..9b897fc 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; -- cgit v0.10.2 From f87f960b2fb802f26ee3b00c19320e57a9c583ff Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 9 Jan 2014 10:27:27 -0600 Subject: 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 diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index 8501954..c613110 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 */ -- cgit v0.10.2 From 68458dede7e0055198318dcb0318df3b4f419507 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 9 Jan 2014 19:36:55 -0800 Subject: 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 diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig index f7ff472..ecdf345 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 a9240a2..e8ebbd4 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 98c4316..15b73d1 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 -- cgit v0.10.2 From b30d49b294fe339719918965594f12eb7967dca0 Mon Sep 17 00:00:00 2001 From: Alex Gal Date: Fri, 10 Jan 2014 15:21:13 -0500 Subject: 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 diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 18a009e..b46b311 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 -- cgit v0.10.2 From 9795229752c31da0c5f8a7dc4c827665327b52f9 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 10 Jan 2014 14:30:41 -0800 Subject: 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 diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 0ed0664..0a8a26e 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 -- cgit v0.10.2 From 09164043f63c947a49797750a09ca1cd7c31108e Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 12 Jan 2014 15:11:37 -0600 Subject: 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 diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ccd24f0a..86b2030 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. -- cgit v0.10.2 From 0673effd41dba323d6a280ef37b5ef29f3f5a653 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 12 Jan 2014 15:11:38 -0600 Subject: 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 diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 7f3d461..54376fd 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 86b2030..c75237e 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, -- cgit v0.10.2 From 452028665312672c6ba9e16a19248ee00ead9400 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 12 Jan 2014 15:11:39 -0600 Subject: 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 diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 5726688..349c776 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) -- cgit v0.10.2 From 1cdbaf0d3410b334255f638a2d69286ce2d775ec Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:29:27 +0530 Subject: 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 diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 30d273c..7b96b3e 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 e9904e5..a40e5c5 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 b041052..5bdc938 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 0000000..3fe2bab2 --- /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 d0c3aec..07fcf16 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, -- cgit v0.10.2 From 78175fc45da3066337fcacbff562097bfda2ce7e Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:29:28 +0530 Subject: ath9k: Show only active TIDs in node_aggr Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index 3fe2bab2..3d11968 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: -- cgit v0.10.2 From c3b9f9e86b178fd3e94e2d6dd6da84ab38fa2ba7 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:29:29 +0530 Subject: ath9k: Use a separate debugfs file for PHY errors Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 5bdc938..ab7264c 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, -- cgit v0.10.2 From 350e2dcb4647d144000db1505c94d586fcd57919 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:29:30 +0530 Subject: 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 diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index f622a98..cba7d77 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 ec02d38..b01b8c3 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 3d11968..d33cc88 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 f7cc5b3..5bf3243 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 e8d0e7f..0a75e2f 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, -- cgit v0.10.2 From 9d89cadd39f78eb4dd70d522dc3fd17b3c41de58 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 07:40:56 +0530 Subject: 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 diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 07fcf16..ca8aff1 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); } -- cgit v0.10.2 From 071aa9a8b2e724679ac2cf14f31b781a3783c377 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 13 Jan 2014 13:55:11 +0530 Subject: 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 diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index cba7d77..b5ac32c 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 b01b8c3..cc7a025 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 ca8aff1..73a3655 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; -- cgit v0.10.2 From 6dc75f1c9ff912cdfea4cf6a2bb5957ea174a9e5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 13 Jan 2014 17:17:26 +0100 Subject: 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 diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 8138f1c..9417cb5 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; -- cgit v0.10.2 From 608cfbe4abaf76e9d732efd7ed1cfa3998163d91 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 13 Jan 2014 22:05:23 +0300 Subject: 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 diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 9c96831..153c615 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 + -- cgit v0.10.2 From 2d93aee152b1758a94a18fe15d72153ba73b5679 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Dec 2013 14:15:41 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index d69b0fb..100bd0d 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 16f66c1..f950780 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. * -- cgit v0.10.2 From cf9d11888091920e356d085a5fa1644fc5c05429 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Dec 2013 18:54:06 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index a4d3bce..90378c2 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); -- cgit v0.10.2 From 099d8f20b65d0dac07fb6e2ca0ed19e5d36d7d2e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 5 Jan 2014 15:09:44 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index c6beb0f..9affd43 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; -- cgit v0.10.2 From 560843f4ab7ba8c2e5edf01949bba4d7a94f01e9 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sun, 5 Jan 2014 21:04:19 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 54887b9..c4f214d 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) { -- cgit v0.10.2 From 01e0efe3178dbeed1284e55b70a3160a279995b2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 6 Jan 2014 09:05:54 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 76cdce9..369d4c9 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; } -- cgit v0.10.2 From 9ddca8607fc1850376a018b3be0a437f0bf99a2f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 09:29:40 +0100 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 208771a..797b4d9 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 -- cgit v0.10.2 From 4af8beaa7b32ca3e7479bf0189f973ac40c1cff6 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 1 Jan 2014 10:05:55 +0200 Subject: iwlwifi: mvm: remove unused flags from add station command Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 6bbbad4..1b60fdf 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), -- cgit v0.10.2 From f94045ed19821cee923f79953f3f35a2de81f695 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 6 Jan 2014 13:38:55 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 797b4d9..e843080 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 -- cgit v0.10.2 From e36b766d0c674c9dc73a2ace7aa37aea78afa233 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Mon, 6 Jan 2014 20:16:48 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 1b61cb5..0c677b4 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) -- cgit v0.10.2 From 65b30348dbf9b529901b5c2b62dca6cad9017a2a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Jan 2014 13:16:33 +0100 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 8a3286a..e2ba4a7 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 b68bb2f..7a1bc1c 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 4da6d90..9980635 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 8d1b5ed..1f065cf 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 f04d2f4..f36a7ee 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 50f3d7f..b4c2aba 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 487d61b..a4a5e25 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 1890ea2..08c23d4 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); -- cgit v0.10.2 From 05159fccf21cde603f7be8815a94ac85e9c0f245 Mon Sep 17 00:00:00 2001 From: Eytan Lifshitz Date: Tue, 7 Jan 2014 12:50:45 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 9affd43..35b71af 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"); -- cgit v0.10.2 From 7a06a38a115c4d8c1b442e544047fb151d5d4e03 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 9 Jan 2014 09:55:38 +0200 Subject: iwlwifi: mvm: add REPLY_SF_CFG_CMD to cmd strings Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 2437a06..a3d43de 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), -- cgit v0.10.2 From cf38e4f756d6396657c76f72ef42b6f47c523f97 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sun, 5 Jan 2014 19:27:24 +0200 Subject: 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 diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 0c677b4..f06f4cb 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, -- cgit v0.10.2 From a74d036f98280b0de312f842ad09c26de56f27c5 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Mon, 13 Jan 2014 22:20:22 +0100 Subject: 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 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 5681b98..57cddee 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 9c7f08a..4f936c6 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 0000000..d5ef86d --- /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 0000000..d454580 --- /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 9fd4067..a74a3d1 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 7ea424e..c7d0dbc 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_ */ -- cgit v0.10.2 From 53036261033abdab7323a4a896a9bde84d2c2f17 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Mon, 13 Jan 2014 22:20:23 +0100 Subject: 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 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 4f936c6..1905789 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 a74a3d1..4342976 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 c7d0dbc..91c61cb 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 { -- cgit v0.10.2 From 3355650c61c4e3d56deadb601762b09ed8a30939 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:25 +0100 Subject: 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 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 1905789..b9d5f73 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 4342976..88eacf1 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 91c61cb..9a72b71 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_ */ -- cgit v0.10.2 From c805eeb7a8e165e218042a747ab41c2dbdc74885 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:26 +0100 Subject: 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 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index 88eacf1..d1de037 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 9a72b71..a1aadf4 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]; -- cgit v0.10.2 From 9cf218fc333406764275eab9b0e47f5c39929e79 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:27 +0100 Subject: 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 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index b9d5f73..ef24a1b 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 d1de037..82bf3c5 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 a1aadf4..fb06143 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_ */ -- cgit v0.10.2 From 2668b0b16cb555abe1b35a1c3cdeb482799fb7cb Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:28 +0100 Subject: 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 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index aad83ae..d7718a5 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); -- cgit v0.10.2 From bb350711ecb86568468b497d0ecd5c3fd7b45b1b Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 13 Jan 2014 22:20:29 +0100 Subject: 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 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 34c993d..fa35b23 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 5c12a076..c453561 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 af39eda..d4d966b 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 ef24a1b..3e99189 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]); - - /* Enable clock for device interrupts */ - brcmf_sdio_bus_sleep(bus, false, false); + 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); + + /* 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; + + /* 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); - /* 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; + /* Turn off the bus (F2), free any pending packets */ + brcmf_dbg(INTR, "disable SDIO interrupts\n"); + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); - /* Change our idea of bus state */ - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; + /* Clear any pending interrupts now that F2 is disabled */ + w_sdreg32(bus, local_hostintmask, + offsetof(struct sdpcmd_regs, intstatus)); - /* 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); + 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 c345c32..24f65cd 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); } -- cgit v0.10.2 From 1908861f2fb072446eabbdc4a01c3bec40ed2f7c Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 14 Jan 2014 09:22:36 +0530 Subject: 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 diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index d33cc88..d76e6e0 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) -- cgit v0.10.2 From 522aaa182a8d942d29e37e1f80b3f5b3c68cc475 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 14 Jan 2014 13:25:17 +0530 Subject: 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 diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 8c145cd..4eb35aa 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; -- cgit v0.10.2 From 3b24e9f8c07fc09174e4f2fe4d0f716595ae2146 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 14 Jan 2014 13:41:17 +0530 Subject: 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 diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 4eb35aa..a352128 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"); -- cgit v0.10.2 From 0786dc4edd2d89053d6366c4fbc6b458ee2e9b5e Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Tue, 14 Jan 2014 20:31:26 +0200 Subject: 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 diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 9b88440..0b0975d 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" -- cgit v0.10.2 From 1e202242ee1432d68a8bea4919b2ae0ef19d9e06 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Tue, 14 Jan 2014 19:16:34 -0800 Subject: 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 diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 4cee6ce..5fa932d 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) -- cgit v0.10.2 From f862bfd1837c36efbb7f3475dbd409ce86edb080 Mon Sep 17 00:00:00 2001 From: Ujjal Roy Date: Tue, 14 Jan 2014 19:22:22 -0800 Subject: 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 diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e7c81ab..f37b403 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; } -- cgit v0.10.2 From f1d267cab2bc6ecd225abfd8d7e130f9a651408f Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:37:42 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index e0ba7cd..b59cfbe 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 8e99540..8b0ac14 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, ...) { -- cgit v0.10.2 From 1cc47a5b2417b319d022d8aa995888d791b1eee1 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:07:15 +0100 Subject: 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 diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 5bf3243..a0ebdd0 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. -- cgit v0.10.2 From aeb0e356ec6e0d11b8369bbbb32ae81b92a1a46b Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:07:16 +0100 Subject: ath9k_htc: use ath_is_mybeacon Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index b41e008..12e0f32 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)) -- cgit v0.10.2 From d44efe21859777784aea9afef934ad67bbb16935 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:11:15 +0100 Subject: ath5k: use ath_is_mybeacon Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index d85c312..ef35da8 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); } -- cgit v0.10.2 From e4e19c031901e95dc7d1cf0a2c9c50525d71651f Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 15 Jan 2014 17:11:39 +0100 Subject: carl9170: use ath_is_mybeacon Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 1b1b207..536bc46a 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; -- cgit v0.10.2